@allocator-one/rcl 4.4.19 → 4.5.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.md +674 -1240
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,150 +1,299 @@
|
|
|
1
|
-
#
|
|
1
|
+
# rcl — Review Council
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Multi-model AI code review in your terminal. Several models review the same
|
|
4
|
+
change in different roles; `rcl` merges their findings into one consensus
|
|
5
|
+
report and marks which findings block.
|
|
4
6
|
|
|
5
|
-
  
|
|
8
|
+
|
|
9
|
+
- [Install](#install)
|
|
10
|
+
- [Prerequisites](#prerequisites)
|
|
11
|
+
- [Quick start](#quick-start)
|
|
12
|
+
- [Commands](#commands)
|
|
13
|
+
- [Configuration](#configuration)
|
|
14
|
+
- [Environment variables](#environment-variables)
|
|
15
|
+
- [How consensus and gating work](#how-consensus-and-gating-work)
|
|
16
|
+
- [Harness evidence and gate](#harness-evidence-and-gate)
|
|
17
|
+
- [Agent skills: `/rcl` and `/rcl-converge`](#agent-skills-rcl-and-rcl-converge)
|
|
18
|
+
- [Changelog](#changelog)
|
|
6
19
|
|
|
7
20
|
---
|
|
8
21
|
|
|
9
22
|
## Install
|
|
10
23
|
|
|
11
24
|
```bash
|
|
12
|
-
npm install -g
|
|
25
|
+
npm install -g @allocator-one/rcl
|
|
13
26
|
```
|
|
14
27
|
|
|
15
|
-
Requires Node.js
|
|
28
|
+
Requires Node.js 20 or later. The command is `rcl`.
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
### Migrating from `review-council`
|
|
18
31
|
|
|
19
|
-
|
|
32
|
+
Earlier releases were published as `review-council`. Both packages install the
|
|
33
|
+
same `rcl` command, and npm refuses to overwrite a command that belongs to
|
|
34
|
+
another package (`EEXIST`), so remove the old package first:
|
|
20
35
|
|
|
21
36
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
npm uninstall -g review-council
|
|
38
|
+
npm install -g @allocator-one/rcl
|
|
39
|
+
```
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
Nothing else changes. Config files keep their names (`.review-council.yml`,
|
|
42
|
+
`.review-council.yaml`, `.review-council.json`) and keep working, and the
|
|
43
|
+
per-machine state in `~/.rcl` and the convergence state under `.git/` are read
|
|
44
|
+
as before.
|
|
27
45
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Prerequisites
|
|
49
|
+
|
|
50
|
+
### Provider API keys
|
|
51
|
+
|
|
52
|
+
The default council calls three providers directly. Set their keys before your
|
|
53
|
+
first review:
|
|
54
|
+
|
|
55
|
+
| Seat | Default model | Key |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| General and specialist reviewers (blocking lane) | `anthropic/claude-opus-5-5`, `openai/gpt-6-sol` | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` |
|
|
58
|
+
| Specialist reviewer (secondary lane) | `google/gemini-3.8-flash` | `GOOGLE_API_KEY` or `GEMINI_API_KEY` |
|
|
59
|
+
| Verifier for single-model findings | `openai/gpt-6-astra` | `OPENAI_API_KEY` |
|
|
60
|
+
| Async bonus reviewer (optional) | `openrouter/moonshotai/kimi-k3` | `OPENROUTER_API_KEY` |
|
|
61
|
+
|
|
62
|
+
A seat whose key is missing fails; failed blocking-lane seats lower the
|
|
63
|
+
report's reviewer health. If `OPENROUTER_API_KEY` is not set, the default async
|
|
64
|
+
reviewer is dropped with a warning; models you configure explicitly fail loudly
|
|
65
|
+
instead. Guarded convergence launches keep the default roster intact and refuse
|
|
66
|
+
before claiming an attempt when any roster provider's key is missing.
|
|
67
|
+
|
|
68
|
+
When the key is set, default reviews also send diff and context content to
|
|
69
|
+
OpenRouter, an aggregator and an additional data processor beyond the direct
|
|
70
|
+
model providers. Configure `models` and `asyncModels` explicitly if that
|
|
71
|
+
matters for your repository. An explicit `models` list (in the config or via
|
|
72
|
+
`--models`) also clears the default secondary and async lists unless you set
|
|
73
|
+
those too.
|
|
74
|
+
|
|
75
|
+
Reviewing a GitHub pull request reads it through the GitHub API. For PR
|
|
76
|
+
fetches and `--post`, rcl uses a nonempty `githubToken` config value, then
|
|
77
|
+
`GITHUB_TOKEN`, then your existing `gh auth token --hostname github.com` login.
|
|
78
|
+
The fallback is noninteractive and bounded; without any credential, public
|
|
79
|
+
repositories still work anonymously, and a PR 404 explains how to check
|
|
80
|
+
private-repository access without exposing credentials. Local patch and
|
|
81
|
+
git-mode reviews read no GitHub credentials.
|
|
82
|
+
|
|
83
|
+
### Providers and model names
|
|
84
|
+
|
|
85
|
+
Name models with a provider prefix:
|
|
86
|
+
|
|
87
|
+
| Prefix | Provider | Credentials |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `anthropic/` | Anthropic API | `ANTHROPIC_API_KEY` |
|
|
90
|
+
| `openai/` | OpenAI API | `OPENAI_API_KEY` |
|
|
91
|
+
| `google/` | Google Gemini API | `GOOGLE_API_KEY`, else `GEMINI_API_KEY` (blank values fall through) |
|
|
92
|
+
| `openrouter/` | [OpenRouter](https://openrouter.ai); keep the vendor segment, e.g. `openrouter/moonshotai/kimi-k3` | `OPENROUTER_API_KEY` (required; never falls back to `OPENAI_API_KEY`) |
|
|
93
|
+
| `openai-compat/` | Any OpenAI-compatible chat completions endpoint (Ollama, LM Studio, a self-hosted gateway) | `OPENAI_COMPAT_BASE_URL` (default `http://localhost:11434/v1`), `OPENAI_COMPAT_API_KEY` (default: the placeholder `local`) |
|
|
94
|
+
|
|
95
|
+
Unprefixed names are routed by how they start: `claude…` to Anthropic;
|
|
96
|
+
`gpt…`, `o1…`, `o3…` and `o4…` to OpenAI; `gemini…` to Google. **Every other
|
|
97
|
+
unprefixed name is routed to `openai-compat`** without a warning, which means
|
|
98
|
+
`http://localhost:11434/v1` unless `OPENAI_COMPAT_BASE_URL` is set. A typo such
|
|
99
|
+
as `opus-5-5` therefore targets a local endpoint, and with no local server that
|
|
100
|
+
seat fails with a connection error. Use provider prefixes.
|
|
101
|
+
|
|
102
|
+
### Keys from Harness (optional)
|
|
103
|
+
|
|
104
|
+
Repositories that carry a committed `.harness-cli/config.json` (discovered
|
|
105
|
+
git-style, walking up from the working directory) can get their provider keys
|
|
106
|
+
from a [Harness](https://harness.infra.one) backend instead of every teammate
|
|
107
|
+
managing them by hand: run `harness login` once, and any provider key
|
|
108
|
+
**missing from the environment** is fetched from `GET /api/v1/model-keys` on
|
|
109
|
+
the host that minted the stored login token, and injected for the run.
|
|
110
|
+
|
|
111
|
+
- Environment variables always win; only missing keys are injected.
|
|
112
|
+
- The stored credential is only ever sent to the host it was minted for, never
|
|
113
|
+
to a URL named by the repository's own config (untrusted input in a cloned
|
|
114
|
+
repository).
|
|
115
|
+
- If the fetch is not possible — not logged in, offline, an older backend
|
|
116
|
+
without the endpoint — rcl prints a one-line note and continues with the
|
|
117
|
+
environment as it is. The fetch runs under a 3-second timeout, and keys are
|
|
118
|
+
never written to disk or logs.
|
|
119
|
+
- Which providers the backend serves is server configuration;
|
|
120
|
+
`RCL_NO_HARNESS_KEYS` disables the mechanism client-side.
|
|
31
121
|
|
|
32
122
|
---
|
|
33
123
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
| Role | Description |
|
|
37
|
-
|------|-------------|
|
|
38
|
-
| 🔍 `general` | Comprehensive review covering all dimensions |
|
|
39
|
-
| 🔒 `security-auditor` | Auth, injection, XSS, CSRF, IDOR, and sensitive data exposure |
|
|
40
|
-
| ⚡ `performance-engineer` | N+1 queries, caching, algorithmic complexity, and memory efficiency |
|
|
41
|
-
| 📐 `api-design` | API contracts, breaking changes, REST/gRPC conventions |
|
|
42
|
-
| 🧪 `test-coverage` | Missing tests, edge cases, flawed test logic |
|
|
43
|
-
| ✏️ `dx-critic` | Readability, naming, documentation, and developer ergonomics |
|
|
44
|
-
| 🏗️ `architecture` | Module boundaries, coupling, and architectural patterns |
|
|
45
|
-
| 🐛 `bug-hunter` | Logic errors, null paths, race conditions, off-by-one |
|
|
46
|
-
| ♿ `accessibility-auditor` | WCAG compliance, ARIA roles, keyboard navigation |
|
|
47
|
-
| 📄 `spec-compliance` | Checks implementation against a spec or plan file |
|
|
48
|
-
| `regression-hunter` | Changed defaults, weakened guards, and lost behavior |
|
|
49
|
-
| `dependency-hygiene` | Unnecessary dependencies, external requests, and privacy leaks |
|
|
50
|
-
| `edge-case-hunter` | Boundary values, unusual inputs, and failure paths |
|
|
124
|
+
## Quick start
|
|
51
125
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
are supplied as shared context to the remaining reviewers. Remove the retired
|
|
55
|
-
names from explicit role lists; they follow the usual unknown-role warning and
|
|
56
|
-
skip behavior unless you define a custom role with that name.
|
|
126
|
+
```bash
|
|
127
|
+
export ANTHROPIC_API_KEY=… OPENAI_API_KEY=… GEMINI_API_KEY=…
|
|
57
128
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
is eligible for specialist assignments only. The async general reviewer
|
|
61
|
-
and verifier are separate; quorum is calculated from the blocking roster.
|
|
129
|
+
# Review your staged changes before committing
|
|
130
|
+
rcl review --staged
|
|
62
131
|
|
|
63
|
-
|
|
132
|
+
# Review a GitHub pull request and keep the full report
|
|
133
|
+
rcl review owner/repo#42 --json-file report.json --markdown report.md
|
|
64
134
|
|
|
65
|
-
|
|
66
|
-
rcl
|
|
67
|
-
rcl roles show security-auditor
|
|
135
|
+
# Fail a CI job when the council reports a blocking finding
|
|
136
|
+
rcl review owner/repo#42 --ci
|
|
68
137
|
```
|
|
69
138
|
|
|
139
|
+
Without `--json-file` or `--markdown`, rcl writes no report files; the terminal
|
|
140
|
+
summary is the only output.
|
|
141
|
+
|
|
70
142
|
---
|
|
71
143
|
|
|
72
|
-
##
|
|
144
|
+
## Commands
|
|
145
|
+
|
|
146
|
+
| Command | Purpose |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| [`rcl review [target]`](#rcl-review-target) | Review a pull request, a patch file, or uncommitted work |
|
|
149
|
+
| [`rcl review-plan <file>`](#rcl-review-plan-file) | Review an implementation plan before code exists |
|
|
150
|
+
| [`rcl discuss <question>`](#rcl-discuss-question) | Ask the models that raised a finding a follow-up question |
|
|
151
|
+
| [`rcl roles`](#rcl-roles) | List and inspect reviewer roles |
|
|
152
|
+
| [`rcl models`](#rcl-models) | Per-model precision, volume, latency and consensus weight |
|
|
153
|
+
| [`rcl evidence …`, `rcl telemetry …`](#rcl-evidence-and-rcl-telemetry) | Read and deliver review evidence on Harness |
|
|
154
|
+
| [`rcl converge-…`](#convergence-commands) | Convergence-loop accounting and recovery |
|
|
155
|
+
|
|
156
|
+
`rcl <command> --help` lists every option.
|
|
73
157
|
|
|
74
158
|
### `rcl review [target]`
|
|
75
159
|
|
|
76
|
-
Review a PR, a local diff, or uncommitted work.
|
|
160
|
+
Review a PR, a local diff, or uncommitted work. Pick exactly one source:
|
|
77
161
|
|
|
78
|
-
|
|
79
|
-
- `
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
162
|
+
- `owner/repo#N` or a GitHub PR URL — a pull request
|
|
163
|
+
- a path to a `.patch` or `.diff` file — a local patch
|
|
164
|
+
- `--staged` — staged changes (`git diff --cached`)
|
|
165
|
+
- `--working-tree` — all uncommitted changes (`git diff HEAD`, staged and
|
|
166
|
+
unstaged)
|
|
83
167
|
|
|
84
|
-
|
|
168
|
+
Untracked files are invisible to `git diff` and therefore not reviewed.
|
|
85
169
|
|
|
86
170
|
| Flag | Description |
|
|
87
|
-
|
|
88
|
-
| `--staged` | Review
|
|
89
|
-
| `--working-tree` | Review all uncommitted changes (`git diff HEAD`, staged + unstaged) |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| `--staged` / `--working-tree` | Review uncommitted changes instead of a target |
|
|
90
173
|
| `--role <name>` | Use a single named role |
|
|
91
|
-
| `--roles <names>` | Comma-separated list of roles |
|
|
92
|
-
| `--reviewer <model:role>` | Explicit model:role pair (repeatable) |
|
|
93
|
-
| `--models <models>` | Comma-separated
|
|
174
|
+
| `--roles <names>` | Comma-separated list of roles (`all` runs every role) |
|
|
175
|
+
| `--reviewer <model:role>` | Explicit model:role pair (repeatable); runs exactly these pairs, with no async seats |
|
|
176
|
+
| `--models <models>` | Comma-separated primary (blocking) models; clears the default secondary and async lists unless those flags are also given |
|
|
177
|
+
| `--secondary-models <models>` | Comma-separated secondary models, used for specialist roles only |
|
|
178
|
+
| `--async-models <models>` | Comma-separated async bonus reviewers, fired with the round and never awaited |
|
|
94
179
|
| `--context <path>` | Context file or directory (repeatable) |
|
|
95
|
-
| `--spec <path>` | Specification file
|
|
96
|
-
| `--
|
|
97
|
-
| `--post` | Post review
|
|
98
|
-
| `--json` | Print JSON
|
|
99
|
-
| `--json-file <path>` | Write JSON
|
|
100
|
-
| `--markdown <path>` | Write Markdown report to a file |
|
|
101
|
-
| `--ci` | Exit
|
|
102
|
-
| `--head-sha <sha>` | Exact head commit a patch file was taken from (patch files only) |
|
|
103
|
-
| `--base-sha <sha>` | Exact base commit a patch file was taken from (patch files only) |
|
|
180
|
+
| `--spec <path>` | Specification file; enables the `spec-compliance` role |
|
|
181
|
+
| `--spec-source <source>` | Where `--spec` came from: `flag`, `repo_file`, or `harness_issue:<ID>` (recorded in the report) |
|
|
182
|
+
| `--post` | Post the review to the pull request; findings that map onto the diff become inline comments |
|
|
183
|
+
| `--json` | Print the JSON report to stdout |
|
|
184
|
+
| `--json-file <path>` | Write the JSON report to a file |
|
|
185
|
+
| `--markdown <path>` | Write the Markdown report to a file |
|
|
186
|
+
| `--ci` | Exit 1 when the review is a CI failure (see [`--ci`](#--ci)) |
|
|
187
|
+
| `--head-sha <sha>` / `--base-sha <sha>` | Exact head/base commit a patch file was taken from (patch files only) |
|
|
104
188
|
| `--expect-head-sha <sha>` | Fail fast unless the resolved head commit equals this SHA |
|
|
105
|
-
| `--
|
|
106
|
-
| `--
|
|
107
|
-
| `--
|
|
108
|
-
| `--
|
|
109
|
-
| `--bound-fix-recovery <run-id>` | Allow one additional review of unchanged inputs after verifying the selected native dismissal-only run against live Harness evidence; requires guarded convergence and required PR evidence |
|
|
110
|
-
| `--launch-intent <intent>` | Guarded intent: `review` (default), `stop-upstream`, `stop-review`, or `retry-delivery` |
|
|
111
|
-
| `--retry-reason <reason>` | Explicit bounded recovery decision for a failed/unknown launch; preserves spent attempts |
|
|
112
|
-
| `--retry-report <path>` | Bind an original legacy report to an inconclusive retry; new inputs may differ; requires `--retry-reason` |
|
|
113
|
-
| `--resume-pending` / `--resume-async-sha256 <hashes>` | Recover one proven dead-owner pending launch while retaining exact async artifacts and treating unknown blocking work as failed |
|
|
114
|
-
| `--ordinary-pending-package <path>` / `--preview-pending` | Authenticate a sealed ordinary pending recovery package without mutation before choosing a recovery operation |
|
|
115
|
-
| `--finalize-pending-only` / `--pending-native-sha256 <digest>` / `--pending-attempt-sha256 <digest>` | Finalize the previewed ordinary pending attempt as failed/unknown without claiming or dispatching its successor |
|
|
116
|
-
| `--max-attempts <n>` / `--max-rounds <n>` | Guarded launch only: explicitly authorized caps; omission preserves native caps |
|
|
117
|
-
| `--attest` | GitHub Actions gate workflow only: exchange the job's OIDC token for a run-bound Harness credential and record the review as attested (see below) |
|
|
189
|
+
| `--for-pr <owner/repo#N>` | Bind a patch-file review to the pull request it was taken from (needs `--head-sha`) |
|
|
190
|
+
| `--no-telemetry` | Do not deliver this review as evidence to Harness |
|
|
191
|
+
| `--evidence-required` | Exit 4 unless Harness acknowledged the evidence |
|
|
192
|
+
| `--attest` | GitHub Actions gate workflow only: record the review as attested (see [The gate workflow](#the-gate-workflow)) |
|
|
118
193
|
| `--config <path>` | Path to a config file |
|
|
119
194
|
|
|
120
|
-
`--role`, `--roles`, and `--reviewer` are mutually exclusive.
|
|
195
|
+
`--role`, `--roles`, and `--reviewer` are mutually exclusive.
|
|
121
196
|
|
|
122
|
-
|
|
197
|
+
`--focus <areas>` is accepted by `rcl review` but has no effect: it is not
|
|
198
|
+
passed to reviewers. (`rcl review-plan --focus` does work.)
|
|
123
199
|
|
|
124
|
-
**
|
|
200
|
+
**Convergence and recovery flags.** These serve the convergence loop and its
|
|
201
|
+
recovery operations, documented in
|
|
202
|
+
[Convergence and recovery](https://github.com/allocator-one/rcl/blob/main/docs/convergence.md):
|
|
203
|
+
|
|
204
|
+
| Flag | Purpose |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `--guarded-converge` | Validate and claim one attempt inside this process; derive the round from native state |
|
|
207
|
+
| `--converge-target <key>` | Convergence target this round belongs to (or `RCL_CONVERGE_TARGET`) |
|
|
208
|
+
| `--round <n>` / `--attempt <n>` | Converge context recorded in the report (or `RCL_CONVERGE_ROUND` / `RCL_CONVERGE_ATTEMPT`); guarded launches derive these themselves |
|
|
209
|
+
| `--start-over` | Start an explicitly requested fresh review cycle with a new normal budget; preserves prior spending and evidence |
|
|
210
|
+
| `--max-attempts <n>` / `--max-rounds <n>` | Guarded launch only: explicitly authorized caps; omission preserves native caps |
|
|
211
|
+
| `--retry-reason <reason>` | Explicit bounded recovery decision for a failed, unknown or inconclusive launch; preserves spent attempts |
|
|
212
|
+
| `--retry-report <path>` | Bind an original legacy (4.1.10–4.1.12) report to an inconclusive retry; requires `--retry-reason` |
|
|
213
|
+
| `--launch-intent <intent>` | `review` (default), `stop-upstream`, `stop-review`, or `retry-delivery` |
|
|
214
|
+
| `--bound-fix-recovery <run-id>` | Allow one more review of unchanged inputs after verifying a native dismissal-only run against live Harness evidence |
|
|
215
|
+
| `--export-pending-package <path>` | Export the authenticated inputs of an ordinary pending launch whose coordinator died to an exclusive private file, without provider calls or native writes |
|
|
216
|
+
| `--expect-base-sha <sha>` | With `--export-pending-package` only: require the resolved current base to equal this SHA |
|
|
217
|
+
| `--preview-pending` | Authenticate a pending recovery or preview a package export without writes or provider calls |
|
|
218
|
+
| `--ordinary-pending-package <path>` | Immutable pending-launch package for `--resume-pending` or `--finalize-pending-only` |
|
|
219
|
+
| `--resume-pending` / `--resume-async-sha256 <hashes>` | Finalize a dead pending launch and claim one checkpointed retry, retaining the exact async results |
|
|
220
|
+
| `--finalize-pending-only` / `--pending-native-sha256 <digest>` / `--pending-attempt-sha256 <digest>` | Finalize the previewed pending attempt as failed/unknown without claiming a successor |
|
|
221
|
+
|
|
222
|
+
**Reports.** Every report carries a `run` header: a client run id (UUIDv7),
|
|
223
|
+
the rcl version, the target with its exact `head_sha`/`base_sha` (from GitHub
|
|
224
|
+
for PRs, from `git rev-parse HEAD` and the merge-base with the remote default
|
|
225
|
+
branch for `--staged`/`--working-tree`, from `--head-sha`/`--base-sha` for
|
|
226
|
+
patch files) and a `diff_sha256`, the roster with each seat's lane
|
|
227
|
+
(`blocking`, `secondary`, `async`, `verification`), a config digest with
|
|
228
|
+
thresholds and gating inline, spec and context-file digests, a best-effort
|
|
229
|
+
`runner` claim (`agent` / `ci` / `human`), timing, the CI verdict (computed
|
|
230
|
+
even without `--ci`), and the converge context when run in a convergence loop.
|
|
231
|
+
Every finding carries an `identity`, allocated uniquely across the report's
|
|
232
|
+
consensus findings, including the below-threshold appendix. Keys use
|
|
233
|
+
`report:<run-id>:<16-hex-key>` so report allocation cannot alias unrelated
|
|
234
|
+
native ledger identities or reuse another run's classification. Colliding
|
|
235
|
+
location anchors are disambiguated before thresholding; native cross-round
|
|
236
|
+
location matching still determines the unchanged canonical ledger identity.
|
|
237
|
+
Every reviewer call records token `usage` where the provider reports it.
|
|
238
|
+
Reports without a `run` header (before 3.0) remain readable; ambiguous
|
|
239
|
+
classifications are refused by `rcl converge-report`.
|
|
240
|
+
|
|
241
|
+
Before dispatch, rcl prints the expanded reviewer × chunk call count,
|
|
242
|
+
concurrency, wave count, timeout, and timeout-bound queue estimate. Interactive
|
|
243
|
+
runs update the spinner; redirected runs emit periodic heartbeat and bounded
|
|
244
|
+
completion lines, so a long queue is distinguishable from a hung process.
|
|
245
|
+
|
|
246
|
+
#### Exit codes
|
|
247
|
+
|
|
248
|
+
| Exit | Meaning |
|
|
249
|
+
| --- | --- |
|
|
250
|
+
| 0 | The review completed (or there was nothing to review); with `--ci`, nothing failed the gate; with `--evidence-required`, Harness acknowledged the evidence |
|
|
251
|
+
| 1 | An error (invalid flags or target, refused launch, configuration or provider setup failure), a `--ci` gate failure, or a requested report file that could not be written |
|
|
252
|
+
| 2 | Guarded convergence only: the attempt or round cap is exhausted; continuing needs an explicitly approved higher cap |
|
|
253
|
+
| 3 | Guarded convergence only: native attempt or round state could not be read or written |
|
|
254
|
+
| 4 | `--evidence-required` (implied by `--attest`): the review completed but Harness did not acknowledge the evidence |
|
|
255
|
+
|
|
256
|
+
When `--ci` fails and evidence delivery also failed, the exit code is 1 and the
|
|
257
|
+
evidence failure is printed beside the gate verdict. A run that starts or
|
|
258
|
+
continues a fresh review cycle (`--start-over`, or a later ordinary review of
|
|
259
|
+
that pull request from the same repository) is a guarded convergence launch
|
|
260
|
+
with evidence required.
|
|
261
|
+
|
|
262
|
+
#### `--ci`
|
|
263
|
+
|
|
264
|
+
`--ci` exits 1 when no reviewer succeeded (an empty finding list then means
|
|
265
|
+
"nobody looked", not "clean") or when the report contains at least one
|
|
266
|
+
**gating** finding. In the default `verified-consensus` gating mode, a
|
|
267
|
+
critical or important finding gates when at least two distinct models raised it
|
|
268
|
+
(`gating.minModels`), when it is critical, or when the verifier confirmed it
|
|
269
|
+
with source evidence; its `gating.reason` is then `consensus`, `critical` or
|
|
270
|
+
`verified`. A refuted or unverifiable single-model claim (`gating.reason:
|
|
271
|
+
none`) is reported but does not fail CI. In `all-findings` mode every critical
|
|
272
|
+
or important finding fails CI. Findings in the below-threshold appendix never
|
|
273
|
+
count. See [How consensus and gating work](#how-consensus-and-gating-work).
|
|
274
|
+
|
|
275
|
+
Examples:
|
|
125
276
|
|
|
126
277
|
```bash
|
|
127
|
-
#
|
|
278
|
+
# Explicit model:role pairs
|
|
128
279
|
rcl review owner/repo#7 \
|
|
129
|
-
--reviewer claude-opus-5-5:security-auditor \
|
|
130
|
-
--reviewer gpt-6-sol:bug-hunter
|
|
280
|
+
--reviewer anthropic/claude-opus-5-5:security-auditor \
|
|
281
|
+
--reviewer openai/gpt-6-sol:bug-hunter
|
|
131
282
|
|
|
132
283
|
# Spec compliance review with context
|
|
133
284
|
rcl review ./feature.patch --role spec-compliance --spec SPEC.md --context src/
|
|
134
285
|
|
|
135
|
-
#
|
|
286
|
+
# JSON for downstream processing
|
|
136
287
|
rcl review owner/repo#99 --json > findings.json
|
|
137
288
|
|
|
138
|
-
# Review
|
|
139
|
-
rcl review --staged
|
|
289
|
+
# Review uncommitted work with two roles
|
|
140
290
|
rcl review --working-tree --roles security-auditor,bug-hunter
|
|
141
291
|
```
|
|
142
292
|
|
|
143
|
-
---
|
|
144
|
-
|
|
145
293
|
### `rcl review-plan <file>`
|
|
146
294
|
|
|
147
|
-
Council-review an implementation plan document (PRD,
|
|
295
|
+
Council-review an implementation plan document (PRD, build plan, design doc)
|
|
296
|
+
before any code exists.
|
|
148
297
|
|
|
149
298
|
```bash
|
|
150
299
|
rcl review-plan docs/plan.md
|
|
@@ -152,15 +301,25 @@ rcl review-plan docs/plan.md --focus risks # feasibility | completeness |
|
|
|
152
301
|
rcl review-plan docs/plan.md --spec PRD.md # also check the plan against a spec
|
|
153
302
|
```
|
|
154
303
|
|
|
155
|
-
The plan flows through the normal pipeline — multi-model dispatch, dedup,
|
|
156
|
-
|
|
157
|
-
|
|
304
|
+
The plan flows through the normal pipeline — multi-model dispatch, dedup,
|
|
305
|
+
consensus, agreement-tier report — with plan-adapted prompts. Finding line
|
|
306
|
+
numbers refer to the plan document's own lines. Categories are reinterpreted
|
|
307
|
+
for plans (`correctness` = infeasible or contradictory steps, `tests` = missing
|
|
308
|
+
validation strategy, `best-practices` = process gaps like rollback or
|
|
309
|
+
migration, …).
|
|
158
310
|
|
|
159
|
-
|
|
311
|
+
Default roles are a plan-suited subset (`general`, `architecture`,
|
|
312
|
+
`edge-case-hunter`, plus `spec-compliance` when a spec is given);
|
|
313
|
+
`--role`/`--roles`/`--reviewer` and config `roles` override as usual. It shares
|
|
314
|
+
the model, context, output, telemetry and config flags of `rcl review`. `--post`
|
|
315
|
+
and `--ci` are not offered: there is no PR to post to, and plan findings are
|
|
316
|
+
judgment calls, not gates.
|
|
160
317
|
|
|
161
|
-
### `rcl discuss
|
|
318
|
+
### `rcl discuss <question>`
|
|
162
319
|
|
|
163
|
-
Ask the models that flagged a finding a follow-up question — one round,
|
|
320
|
+
Ask the models that flagged a finding a follow-up question — one round,
|
|
321
|
+
reconstructed from a saved report. Useful when triaging, especially for
|
|
322
|
+
**disputed** findings, where the report shows each model's position.
|
|
164
323
|
|
|
165
324
|
```bash
|
|
166
325
|
rcl review --staged --json-file report.json
|
|
@@ -171,966 +330,134 @@ rcl discuss --report report.json --finding f003 --context src/auth.ts "Does the
|
|
|
171
330
|
rcl discuss --report report.json --finding f003 --models anthropic/claude-opus-5-5 "Summarize the strongest counterargument."
|
|
172
331
|
```
|
|
173
332
|
|
|
174
|
-
Model-generated finding ids can collide; when `--finding <id>` is ambiguous the
|
|
175
|
-
|
|
176
|
-
|
|
333
|
+
Model-generated finding ids can collide; when `--finding <id>` is ambiguous the
|
|
334
|
+
error lists `<id>:<n>` disambiguators. Findings in the below-threshold appendix
|
|
335
|
+
are addressable too. Answers come back in parallel, respecting the configured
|
|
336
|
+
`timeout`, `maxRetries`, and `reasoningEffort`. There is no session state: each
|
|
337
|
+
`discuss` is one independent round built from the report file.
|
|
177
338
|
|
|
178
339
|
### `rcl roles`
|
|
179
340
|
|
|
180
341
|
```bash
|
|
181
|
-
rcl roles list #
|
|
182
|
-
rcl roles show <name> #
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
### Start a fresh review
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
rcl review owner/repo#123 --start-over
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
This reviews the full current inputs again, even on an unchanged head or after an exhausted previous cycle. RCL archives the original native state and spending, creates a Harness review cycle, and starts at attempt 1 / round 1 with the normal 20-attempt / 15-round budget. Previous approvals, dismissals and reviewer responses stay historical. Explicit `--max-attempts` / `--max-rounds` select different limits for the new cycle; old overrides are not inherited.
|
|
194
|
-
|
|
195
|
-
The command enables guarded launch and chooses private JSON/Markdown paths under the Git common directory when omitted. It prints the cycle and cumulative prior spending. Keep the files in place. A captured patch can use the same flag with `--for-pr owner/repo#123 --head-sha <captured-head>`. Full Harness evidence and a user/API actor credential are required; unbound local and attested starts are refused.
|
|
196
|
-
|
|
197
|
-
An unfinished start resumes with the same command and operation, keeping spent claims. A durable terminal dispatch record marks completion, including a recorded failure. If the command output or acknowledgement is lost or uncertain, inspect the native current operation and retained launch/report before retrying; reuse a completed result instead of blindly repeating `--start-over`. An invocation after terminal completion represents a new request; identical command text cannot distinguish an acknowledgement retry from a later deliberate fresh request. No user-supplied operation ID or additional confirmation is required. Ordinary `rcl review owner/repo#123` discovers the local cycle and continues its existing budget; it does not replenish it. A concurrent fresh request cannot silently create another cycle after waiting for the first.
|
|
198
|
-
|
|
199
|
-
Stop an active review through its recorded host task before replacing it. Late reports and verdicts remain bound to their original cycle. After checking reviewer health and exact-head freshness, admit the report with `converge-report`; use `converge-verdict --run-id <report-run-id>` for triage in a fresh cycle. All native, enforced and CI merge gates still apply. To complete missing reviewers while preserving successful work, use the supported recovery workflow instead of starting over.
|
|
200
|
-
|
|
201
|
-
### Guarded convergence launches
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
rcl review change.patch --guarded-converge --converge-target repo-123 \
|
|
205
|
-
--head-sha <captured-head> --base-sha <captured-base> --json-file fresh-report.json
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Keep this command foreground inside a persistent host task/session. It validates
|
|
209
|
-
inputs, credentials and fresh output paths before claiming; native target
|
|
210
|
-
ownership spans claim through completion. Do not call `converge-attempt` first
|
|
211
|
-
or supply `--attempt`. RCL derives the next round from admitted state and rejects
|
|
212
|
-
a conflicting `--round`. Existing caps and all spent attempts are retained.
|
|
213
|
-
Guarded assignment order is stable and missing credentials never shrink the roster.
|
|
214
|
-
|
|
215
|
-
Process and triage the original report before another launch. Unchanged reviewed
|
|
216
|
-
inputs, including mere upstream base-tip movement, do not need another council.
|
|
217
|
-
A real fix needs a fresh resulting head; unresolved native blockers refuse another
|
|
218
|
-
launch. Unknown/failed dispatch requires an explicit `--retry-reason` after
|
|
219
|
-
recovery, even if the head changed. This does not refund attempts or promise
|
|
220
|
-
exactly-once provider billing. Credential presence cannot prove provider availability.
|
|
221
|
-
|
|
222
|
-
For a 4.1.10, 4.1.11 or 4.1.12 aggregate-only completion, retain the original report and
|
|
223
|
-
config and add `--retry-report original-report.json` with an explicit bounded
|
|
224
|
-
`--retry-reason` and a fresh `--json-file` destination. RCL binds the exact original
|
|
225
|
-
report, config, target, head, input, round, attempt, cycle and roster before deriving
|
|
226
|
-
blocking-only health with the shared quorum policy. The new launch may review changed
|
|
227
|
-
current inputs, but its original proof stays bound to those original identities. When
|
|
228
|
-
roles or verifier defaults have since changed, RCL reconstructs the producer version's
|
|
229
|
-
deterministic roster from that bound config; unknown, substituted or ambiguous identities
|
|
230
|
-
still refuse before a claim. Healthy or ambiguous evidence refuses.
|
|
231
|
-
The 4.1.10 producer predates review cycles and is accepted only when the retained
|
|
232
|
-
report, native state and attempt state all remain cycle-free.
|
|
233
|
-
An unadmitted source retries its pending round; an exact latest admitted source
|
|
234
|
-
whose blocking health was inconclusive continues at the next native round. Its
|
|
235
|
-
original admission, findings, verdicts and spent claims remain unchanged. The
|
|
236
|
-
new claim retains the source bytes and binding; no history or budget is reset.
|
|
237
|
-
|
|
238
|
-
For an ordinary pending launch whose coordinator is provably dead, supply an independently reconstructed immutable package that binds the original target, head, base, guarded input, attempt, round, PID and retained async artifact descriptors. RCL recomputes its production guarded-input digest and checks every binding under the target lock before it mutates native state.
|
|
239
|
-
|
|
240
|
-
Use `--preview-pending` with either `--resume-pending` or `--finalize-pending-only` and `--ordinary-pending-package <path>` to authenticate that package with zero state, output or provider writes. Combined `--resume-pending` repeats the checks, marks the spent attempt failed with blocking outcome unknown, archives the exact retained async artifacts, and claims exactly the next checkpointed attempt under its explicitly bounded cap.
|
|
241
|
-
|
|
242
|
-
Use `--finalize-pending-only` when recovery must stop at that failed/unknown finalization. Pass the unchanged cap plus the exact `nativeStateSha256` and `attemptStateSha256` returned by its preview as `--pending-native-sha256` and `--pending-attempt-sha256`. Apply archives the retained async artifacts idempotently and returns a source-bound receipt while leaving the attempt counter, cap, round history and next-free ordinal unchanged. Repeating the command reads back the same receipt; it never creates a successor claim, checkpoint, reviewer callback or provider call. Receipt readback validates either the exact finalized state or a monotonic successor against retained source snapshots. An exact legacy receipt without those snapshots is upgraded idempotently before a successor proceeds, without changing native state or accounting. A later guarded convergence process may claim the next attempt independently.
|
|
243
|
-
|
|
244
|
-
When native triage reports `converged-dismissal-only` but Harness still reports
|
|
245
|
-
`fixes_pending` with no actionable findings, an explicit recovery can authorize
|
|
246
|
-
one additional review of the same inputs:
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
rcl review change.patch --guarded-converge --converge-target repo-123 \
|
|
250
|
-
--for-pr owner/repo#123 --head-sha <captured-head> --base-sha <captured-base> \
|
|
251
|
-
--evidence-required --bound-fix-recovery <latest-admitted-run-id> \
|
|
252
|
-
--json-file fresh-recovery-report.json
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
Reuse the original review inputs and configuration, including the specification
|
|
256
|
-
and reviewer roster; both the head and effective input digest must match the
|
|
257
|
-
latest completed, healthy, delivered and admitted native launch. Its resolution
|
|
258
|
-
must be `converged-dismissal-only` with `fixedThisRound: 0`. Use a PR target or
|
|
259
|
-
an explicit `--for-pr` binding, and explicitly supply `--guarded-converge` and
|
|
260
|
-
`--evidence-required`. This mode rejects `--start-over`, `--attest`,
|
|
261
|
-
`--retry-report`, `--retry-reason`, git working-tree/staged reviews, and launch
|
|
262
|
-
intents other than `review`.
|
|
263
|
-
|
|
264
|
-
Before claiming or dispatching reviewers, RCL reads authenticated live Harness
|
|
265
|
-
status and the selected run. The PR must be unmerged at the exact reviewed head;
|
|
266
|
-
its advisory projection must be conclusive, `fixes_pending`, have zero actionable
|
|
267
|
-
findings, and name that same run. The recorded run must match the repository, PR,
|
|
268
|
-
head, convergence target and native round. Exposed classification and legacy
|
|
269
|
-
pending fields must be clear, and an exposed bound classification protocol must
|
|
270
|
-
be version 1. Unanswered, malformed or mismatched evidence refuses recovery.
|
|
271
|
-
|
|
272
|
-
Harness currently exposes `fixes_pending` for the whole PR and does not provide
|
|
273
|
-
proof identifying which convergence target owns a retained fix obligation.
|
|
274
|
-
These checks establish the native/server mismatch; they cannot establish that
|
|
275
|
-
another round on the selected target will clear it. Recovery therefore allows
|
|
276
|
-
only one claimed attempt per target, PR and head in the current native attempt
|
|
277
|
-
ledger, even if that attempt fails or a later run remains `fixes_pending`.
|
|
278
|
-
The claim durably retains its source run, binding, server status and response
|
|
279
|
-
hashes. Existing attempt and round caps still apply. Process and deliver the new
|
|
280
|
-
report normally; matching enforced evidence and CI remain required for merging.
|
|
281
|
-
|
|
282
|
-
`--launch-intent stop-upstream` never cancels review. `stop-review` and
|
|
283
|
-
`retry-delivery` refuse new reviewer dispatch; cancel an existing review only
|
|
284
|
-
through its retained host handle. Retry evidence with `rcl telemetry flush --run
|
|
285
|
-
<run-id>`, not another council. Intent interpretation and finding adjudication
|
|
286
|
-
remain human/agent decisions; native/enforced evidence and CI still gate merging.
|
|
287
|
-
|
|
288
|
-
### `rcl converge-attempt`
|
|
289
|
-
|
|
290
|
-
Low-level accounting command retained for legacy callers. The generated
|
|
291
|
-
`rcl-converge` skill instead uses `review --guarded-converge`; do not preclaim
|
|
292
|
-
an attempt for that path.
|
|
293
|
-
Each call atomically and durably consumes one per-target attempt under the
|
|
294
|
-
repository's common Git directory, so the budget survives sessions, linked
|
|
295
|
-
worktrees, and abrupt system restarts.
|
|
296
|
-
New targets default to twenty attempts, but an explicit invocation can set any
|
|
297
|
-
positive cap with `--max-attempts`. Omitting the flag on resume preserves the
|
|
298
|
-
persisted cap. At the boundary, RCL refuses before provider calls and directs
|
|
299
|
-
the workflow to ask the user; an approved continuation explicitly supplies a
|
|
300
|
-
higher cap.
|
|
301
|
-
|
|
302
|
-
At the skill level, `--max-attempts N` controls this machine launch budget.
|
|
303
|
-
The separate `--max-rounds N` flag caps evidence rounds and is machine-enforced
|
|
304
|
-
by `rcl converge-report` (default 15, valid range 2–99; rounds past 99 are
|
|
305
|
-
impossible under any flag). The default is a consent boundary, not a stop: at
|
|
306
|
-
15 rounds the workflow asks the user, and an approved continuation supplies a
|
|
307
|
-
higher `--max-rounds`.
|
|
308
|
-
|
|
309
|
-
Full-fleet reviewer completion is not required. Reviewer health counts only
|
|
310
|
-
the report roster's blocking seats, and a seat counts only when every one of
|
|
311
|
-
its chunks succeeded. A round is conclusive when at least
|
|
312
|
-
`max(2, ceil(2 × blocking seats / 3))` blocking seats completed, or more under
|
|
313
|
-
a stricter configured `quorumFraction`. Each report records this as
|
|
314
|
-
`stats.blockingHealth` (`seats`, `successful`, `required`, `conclusive`).
|
|
315
|
-
Secondary, async and verification results keep their findings but never count
|
|
316
|
-
toward the quorum, so the aggregate `stats.successfulReviews` /
|
|
317
|
-
`stats.totalReviews` are informational only: 11 of 17 blocking seats plus one
|
|
318
|
-
async success is 12/18 in aggregate yet inconclusive, because 12 blocking
|
|
319
|
-
seats are required. This is the rule Harness applies to delivered evidence.
|
|
320
|
-
Round closure uses the same seats and policy: bonus successes never cancel
|
|
321
|
-
unfinished blocking reviewers. Every timeout or error must be disclosed, and a
|
|
322
|
-
result below the requirement is inconclusive.
|
|
323
|
-
|
|
324
|
-
Exit code 2 means the configured cap was exhausted and explicit continuation
|
|
325
|
-
approval is required. Exit code 3 means attempt accounting itself failed
|
|
326
|
-
(state, lock, Git, filesystem, or another infrastructure error); increasing
|
|
327
|
-
the cap is not the remedy. With `--json`, failures are emitted as structured
|
|
328
|
-
JSON on stderr. If the attempt is durably recorded but final lock release
|
|
329
|
-
fails, the claim still succeeds with a warning so retrying cannot spend a
|
|
330
|
-
second slot for the same intended launch.
|
|
331
|
-
|
|
332
|
-
The short accounting mutex is fully written as a private owner file and then
|
|
333
|
-
published with an exclusive hard link, which cannot replace an existing file
|
|
334
|
-
or legacy directory. State contents and, where supported, their directory
|
|
335
|
-
entry are synced before a claim succeeds. A dead owner is isolated through a
|
|
336
|
-
token-scoped hard-link tombstone before another claimant can proceed; inode
|
|
337
|
-
checks make that tombstone safe to remove after reclamation. Invalid or legacy
|
|
338
|
-
ownerless locks fail closed, and timeout errors include the manual recovery
|
|
339
|
-
path. When upgrading,
|
|
340
|
-
an evidence ledger seeds only its highest recorded round: historical failed or
|
|
341
|
-
missing-report launches cannot be reconstructed, while every claim after the
|
|
342
|
-
machine state is created is counted exactly. The state remains a same-user
|
|
343
|
-
local safety mechanism, not a tamper-proof store: deliberately deleting
|
|
344
|
-
`.git/rcl-converge-attempts` is an explicit policy bypass.
|
|
345
|
-
|
|
346
|
-
```bash
|
|
347
|
-
rcl converge-attempt --target owner-repo-123 # default/persisted cap
|
|
348
|
-
rcl converge-attempt --target owner-repo-123 --max-attempts 10 # explicit override
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
---
|
|
352
|
-
|
|
353
|
-
### `rcl converge-report` and `rcl converge-verdict`
|
|
354
|
-
|
|
355
|
-
The cross-round memory of a converge run, persisted in
|
|
356
|
-
`.git/rcl-converge-runs/<target>.json`.
|
|
357
|
-
|
|
358
|
-
`converge-report` dedupes one round's report JSON against every prior round of
|
|
359
|
-
the run using a location-anchored finding identity (hash of file + category +
|
|
360
|
-
line bucket, plus a line-overlap matcher — titles are deliberately ignored:
|
|
361
|
-
models rephrase ~98% of them between rounds). Each finding is classified
|
|
362
|
-
`new`, `repeat`, `suppressed` (previously dismissed — a dismissal is terminal
|
|
363
|
-
on its evidence and fresh corroboration alone never reopens it), or `regating`
|
|
364
|
-
(previously dismissed at non-critical severity, now sighted as critical —
|
|
365
|
-
genuinely new evidence). The same call enforces the evidence-round cap:
|
|
366
|
-
default 15, `--max-rounds` accepts 2–99, and rounds past 99 are impossible. Exit
|
|
367
|
-
code 2 is the cap consent boundary; exit 3 is a state failure.
|
|
368
|
-
|
|
369
|
-
Before reading or writing native state, `converge-report` derives blocking
|
|
370
|
-
reviewer health from the report's roster and rows. An inconclusive report exits
|
|
371
|
-
4 (`report_health_inconclusive`) and is not admitted: its findings are not
|
|
372
|
-
classified or triaged. The refusal names the completed blocking seats, the
|
|
373
|
-
required count, every missing, failed or canceled seat, and the excluded
|
|
374
|
-
secondary/async successes (with `--json`, as `error.reviewerHealth`). A report
|
|
375
|
-
whose recorded `stats.blockingHealth` disagrees with its rows exits 4 with
|
|
376
|
-
`report_health_unverifiable`. The report, its attempt and all earlier rounds
|
|
377
|
-
stay unchanged. Continue with the same guarded review command plus
|
|
378
|
-
`--retry-reason`: the guard records blocking health for every new launch,
|
|
379
|
-
requires that explicit reason for an inconclusive one, and spends one more
|
|
380
|
-
attempt at the same round without resetting caps or cycle history. Retained
|
|
381
|
-
missing-reviewer recovery remains a separate workflow. Conclusive health does
|
|
382
|
-
not replace triage, and merging still requires matching enforced review and CI.
|
|
383
|
-
|
|
384
|
-
A report key must identify one canonical identity, status and suppression reason.
|
|
385
|
-
`converge-report` refuses conflicting mappings with exit 3 before writing the
|
|
386
|
-
round state, even when telemetry is off. Reports without finding keys use the
|
|
387
|
-
canonical identity as a fallback and are subject to the same check. This leaves
|
|
388
|
-
ambiguous older reports readable but not classifiable by this command. Preserve
|
|
389
|
-
the original report and ledger for separately supported finding-ref recovery;
|
|
390
|
-
rewriting published evidence or rerunning an unchanged council is not recovery.
|
|
391
|
-
Identical mappings still deduplicate, and native ledger keys and verdicts do not
|
|
392
|
-
change when new run-scoped report keys appear. Until the current run's
|
|
393
|
-
classification is delivered, older runs' aliases cannot resolve its new report
|
|
394
|
-
keys. A report without a current classification does not inherit prior native
|
|
395
|
-
verdicts, even when its findings look unchanged.
|
|
396
|
-
|
|
397
|
-
`converge-verdict` records triage outcomes per finding identity —
|
|
398
|
-
`--fixed <key>` and `--dismissed '<key>=<reason>'` (both repeatable) — which
|
|
399
|
-
drives later-round suppression and accrues the per-model precision history.
|
|
400
|
-
Add `--fixed-reason '<key>=<reason>'` to attach the current fix explanation to
|
|
401
|
-
an identity also passed to `--fixed`. A fixed verdict without this option clears
|
|
402
|
-
any prior explanation; it never reuses an earlier dismissal reason. Each identity
|
|
403
|
-
may appear once per command, and each fixed reason must be nonempty and unique.
|
|
404
|
-
Once every gating identity of the current round is triaged, it also reports
|
|
405
|
-
the round's resolution: `converged-dismissal-only` (everything dismissed,
|
|
406
|
-
nothing fixed — the round converges on the spot, no confirmation round),
|
|
407
|
-
`fixes-pending-fresh-round`, or `unresolved` with the identities still open.
|
|
408
|
-
|
|
409
|
-
```bash
|
|
410
|
-
rcl converge-report --target rcl-30 --report report-r2.json --round 2 --json
|
|
411
|
-
rcl converge-verdict --target rcl-30 --round 2 \
|
|
412
|
-
--fixed 9787c6ea72ae778c \
|
|
413
|
-
--fixed-reason '9787c6ea72ae778c=callback failures now have a distinct outcome' \
|
|
414
|
-
--dismissed 'd2baf9675eb450f0=guard already exists'
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### `rcl converge-stale`
|
|
418
|
-
|
|
419
|
-
A healthy report that became materially stale before admission must be retained without
|
|
420
|
-
assigning its findings or verdicts. The guarded launch refuses with `report_not_admitted`
|
|
421
|
-
and prints the current head and effective input digest. Inspect the actual changes and
|
|
422
|
-
use those exact values to preview a disposition:
|
|
423
|
-
|
|
424
|
-
```bash
|
|
425
|
-
rcl converge-stale --preview --manifest stale.json --target repo-123 \
|
|
426
|
-
--head <current-head> --input-sha256 <current-effective-input-sha256> \
|
|
427
|
-
--report original.json --report-sha256 <original-sha256> \
|
|
428
|
-
--reason "Committed changes and the current specification supersede this report"
|
|
429
|
-
rcl converge-stale --apply --manifest stale.json --manifest-sha256 <reviewed-manifest-sha256>
|
|
430
|
-
# After an interrupted apply, use the same immutable manifest:
|
|
431
|
-
rcl converge-stale --resume --manifest stale.json --manifest-sha256 <reviewed-manifest-sha256>
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
Preview writes only its exclusive manifest. Apply retains the original report and exact
|
|
435
|
-
native snapshots, then atomically adds a digest-bound audit entry under native target ownership.
|
|
436
|
-
Immutable shared objects and reconstructible snapshot templates avoid copying the full
|
|
437
|
-
report and growing history for every correction. Incremental prefix hashing verifies
|
|
438
|
-
the growing audit without repeatedly serializing all earlier entries.
|
|
439
|
-
It does not admit findings, claim attempts, flush evidence, raise caps, or approve a PR.
|
|
440
|
-
Resume is idempotent. Continue with the original `review --guarded-converge` invocation;
|
|
441
|
-
it recomputes the real head/input digest and checks every retained receipt before claiming
|
|
442
|
-
one normal attempt at the next native ordinal. Admission of the fresh report rechecks
|
|
443
|
-
that retained history. The stale attempt stays spent.
|
|
444
|
-
|
|
445
|
-
Before any stale disposition, unchanged inputs require normal admission; upstream tip movement alone is insufficient.
|
|
446
|
-
After disposal, the original report stays historical even if its inputs return. Inspect and
|
|
447
|
-
apply those inputs as another replacement to obtain a fresh review without reviving that report.
|
|
448
|
-
Unknown outcomes, unhealthy reports, pending delivery, missing original evidence, changed
|
|
449
|
-
state and unresolved earlier findings refuse safely. A disposition is bound to one exact
|
|
450
|
-
replacement input. If inputs change again before continuation, inspect the new inputs and
|
|
451
|
-
create a new preview and apply operation; it appends another inspected replacement for the
|
|
452
|
-
same preserved report. Every earlier inspected replacement remains usable: returning to
|
|
453
|
-
one needs only the guarded review command, not another disposition. Preview refuses a
|
|
454
|
-
duplicate replacement plan. Missing or changed retained evidence, forged manifests and
|
|
455
|
-
accidentally truncated audit history are refused before an attempt is claimed. Preview/apply
|
|
456
|
-
bind the then-current native state; later legitimate native transitions remain authoritative.
|
|
457
|
-
This local audit does not authenticate arbitrary edits to the entire native state file.
|
|
458
|
-
Keep the audit directory with its original repository location: repository relocation and
|
|
459
|
-
reconstruction of lost evidence are not supported by this command. Every receipt remains
|
|
460
|
-
required, including earlier inspected alternatives. Native convergence, enforced review and CI remain required.
|
|
461
|
-
|
|
462
|
-
### `rcl converge-gap`
|
|
463
|
-
|
|
464
|
-
A paid attempt and an admitted report round are separate counters. If an original
|
|
465
|
-
later report already carries round 3 while native history ends at round 1, preserve
|
|
466
|
-
that original. Do not relabel it, create an empty round 2, reset budgets, or launch
|
|
467
|
-
reviewers again for bookkeeping.
|
|
468
|
-
|
|
469
|
-
For one explicitly evidenced missing terminal report, preview a local audit:
|
|
470
|
-
|
|
471
|
-
```bash
|
|
472
|
-
rcl converge-gap --preview --manifest gap.json --target rcl-81 \
|
|
473
|
-
--gap-round 2 --admitting-round 3 --attempt 2 --run <original-run-uuid> \
|
|
474
|
-
--report original-r3.json --report-sha256 <original-json-sha256> \
|
|
475
|
-
--incomplete terminal-incomplete.md --incomplete-sha256 <original-evidence-sha256>
|
|
476
|
-
rcl converge-gap --apply --manifest gap.json --manifest-sha256 <preview-manifest-sha256>
|
|
477
|
-
# After interruption, reuse exactly the reviewed manifest and original sources:
|
|
478
|
-
rcl converge-gap --resume --manifest gap.json --manifest-sha256 <preview-manifest-sha256>
|
|
479
|
-
rcl converge-report --target rcl-81 --report original-r3.json --round 3 --json
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
Preview reads bounded original files and the native/attempt ledgers; its only write
|
|
483
|
-
is the requested exclusive manifest. An optional `--evidence <json-path>` supplies
|
|
484
|
-
an array of additional `{ "path": "...", "sha256": "..." }` selections. Apply and
|
|
485
|
-
resume accept only that manifest and its exact byte digest. They share target
|
|
486
|
-
ownership with ordinary writers, retain exact original native, attempt and source
|
|
487
|
-
bytes, and append audit checkpoints before admission becomes available. They leave
|
|
488
|
-
rounds, findings, verdicts, severities, attempts used and caps unchanged. The
|
|
489
|
-
controller exit stays `unknown`; supplied files do not prove global absence.
|
|
490
|
-
Neither audit mode flushes the outbox or sends server events.
|
|
491
|
-
|
|
492
|
-
This version supports one missing ordinal immediately before the selected original
|
|
493
|
-
report, with an explicit spent record for both ordinals and every earlier ordinary
|
|
494
|
-
round present from round 1. Histories with earlier gaps, including audited gaps,
|
|
495
|
-
are unsupported. Migrated totals without those records, multiple gaps, altered
|
|
496
|
-
sources and unsupported storage refuse.
|
|
497
|
-
The later report keeps its original round, run and contents. Later discovery of
|
|
498
|
-
the missing report needs separate explicit evidence recovery; an ordinary empty
|
|
499
|
-
report cannot fill the reserved gap. Audit is local history, never reviewer health,
|
|
500
|
-
convergence or gate approval. Existing v1 clients preserve its additive metadata
|
|
501
|
-
and still refuse an unadmitted jump, but do not validate the new receipt protocol.
|
|
502
|
-
Use this version for gap admission and recovery; no new backend capability is claimed.
|
|
503
|
-
|
|
504
|
-
---
|
|
505
|
-
|
|
506
|
-
Structured findings include the recorded verifier model and explanation, when present,
|
|
507
|
-
at both `findings` and `full` telemetry levels. The JSON report and wire envelope
|
|
508
|
-
share normalization: blank values are absent, model identifiers are capped at 500
|
|
509
|
-
Unicode code points, and explanations at 2,000. Missing legacy explanations are
|
|
510
|
-
never generated. Unicode normalization forms and gate outcomes are unchanged.
|
|
511
|
-
|
|
512
|
-
Quoted credential assignments are redacted through their closing quote or end of
|
|
513
|
-
input, including multiline and unfinished values. Preliminary text truncation
|
|
514
|
-
keeps only through the last whitespace in its bounded prefix (or only an ellipsis
|
|
515
|
-
if there is none), preventing partial secrets from surviving redaction. The shared
|
|
516
|
-
fixture `test/fixtures/verification-normalization.json` comes from allocator-one
|
|
517
|
-
PR #8974 and pins the receiver contract. This corrects the quoted-value and
|
|
518
|
-
preliminary-truncation behavior of RCL 3.6.0. Existing source reports are not
|
|
519
|
-
rewritten to add explanations; legacy backfill retains its declared artifact
|
|
520
|
-
scrubbing and deterministic source identity rules.
|
|
521
|
-
|
|
522
|
-
### `rcl telemetry status`, `flush` and `rejected`
|
|
523
|
-
|
|
524
|
-
Evidence delivery to Harness (epic IO-12475). In a repository that carries
|
|
525
|
-
`.harness-cli/config.json` and with a `harness login` (or `HARNESS_API_TOKEN` +
|
|
526
|
-
`HARNESS_API_URL` in CI), every `rcl review` / `rcl review-plan` records the
|
|
527
|
-
run on Harness after the report is written: the self-describing `run` header,
|
|
528
|
-
one row per consensus finding (with its stable identity), one row per reviewer
|
|
529
|
-
call (status, latency, token usage), the report's `stats`, and — at the default
|
|
530
|
-
`full` level — the JSON and Markdown reports exactly as written, digest-checked
|
|
531
|
-
by the server. The converge commands report their events (attempt claims, cap
|
|
532
|
-
changes, processed rounds, verdicts, resolutions) the same way. Never sent:
|
|
533
|
-
provider API keys, `GITHUB_TOKEN`, the Harness credential, environment
|
|
534
|
-
variables, prompts or raw model answers; every free-text field is truncated
|
|
535
|
-
and scrubbed for key-shaped strings before it leaves the process.
|
|
536
|
-
|
|
537
|
-
The review never blocks on the network. A retryable delivery outage is
|
|
538
|
-
spooled to `~/.rcl/outbox/<run id>/` and retried, with its original run id,
|
|
539
|
-
at the start of every rcl command (bounded to five seconds) or by
|
|
540
|
-
`rcl telemetry flush`. One dim status line says what happened:
|
|
541
|
-
`Evidence recorded: <url>`, `Evidence spooled (Harness unreachable); run rcl
|
|
542
|
-
telemetry flush`, or `Evidence not sent: <host> has not enabled review
|
|
543
|
-
evidence for this organization`. `--evidence-required` exits 4 when the
|
|
544
|
-
evidence is incomplete: the envelope was spooled or refused, the organization
|
|
545
|
-
has evidence off, or a declared artifact did not land (a patch file then needs
|
|
546
|
-
`--head-sha`, and the flag contradicts `--no-telemetry` / `RCL_TELEMETRY=off`).
|
|
547
|
-
Only a spooled delivery is worth `rcl telemetry flush --run <id>`; the status
|
|
548
|
-
line says which. Under `--ci` the gate verdict keeps its exit code and the
|
|
549
|
-
evidence failure is printed beside it. The first delivery from a machine
|
|
550
|
-
prints a one-time notice naming the host and what is sent
|
|
551
|
-
(`~/.rcl/telemetry-notice` records it).
|
|
552
|
-
|
|
553
|
-
Completed envelopes are validated locally before transmission. Two valid
|
|
554
|
-
reversed line numbers are ordered before finding identity is allocated, with
|
|
555
|
-
the original numeric pair retained as parser provenance. Missing, blank,
|
|
556
|
-
boolean, negative, fractional or non-finite coordinates remain parser errors;
|
|
557
|
-
valid sibling findings are preserved. Provenance delivery requires the server
|
|
558
|
-
to advertise evidence protocol version 2.
|
|
559
|
-
|
|
560
|
-
Local validation failures and terminal server refusals retain the exact JSON
|
|
561
|
-
and Markdown bytes in `~/.rcl/quarantine/<run id>/` (or under `RCL_DATA_DIR`).
|
|
562
|
-
The immutable manifest records original digests, delivery mode and diagnostics;
|
|
563
|
-
distinct later delivery observations are appended separately. Interrupted or
|
|
564
|
-
corrupted entries are reported as incomplete. Retention failure, including a
|
|
565
|
-
read-only filesystem or the 1 GiB storage cap, is reported explicitly.
|
|
566
|
-
`rcl telemetry rejected` inspects these files and verifies their digests without
|
|
567
|
-
contacting Harness, flushing the outbox, changing native review accounting or
|
|
568
|
-
applying recovery. Retained evidence is not server acknowledgment. Attested
|
|
569
|
-
evidence is never queued for replay with ordinary credentials. Recovery of an
|
|
570
|
-
already-completed historical run remains a separate operation.
|
|
571
|
-
|
|
572
|
-
If an envelope is slow to acknowledge, use `telemetry flush --envelope-timeout-ms`
|
|
573
|
-
with an integer from 1 to 120000 milliseconds. It changes only the envelope POST
|
|
574
|
-
timeout (default: 10000 ms); artifact transfers keep their 120000 ms ceiling,
|
|
575
|
-
and ordinary reads and events keep their existing timeouts. Shorter caller
|
|
576
|
-
deadlines and known attested credential lifetimes still apply. A timeout leaves
|
|
577
|
-
the evidence queued for a later flush; this option does not rerun reviewers.
|
|
578
|
-
|
|
579
|
-
```bash
|
|
580
|
-
rcl telemetry status # level, credential source, what waits in the outbox
|
|
581
|
-
rcl telemetry flush # deliver everything spooled, to completion
|
|
582
|
-
rcl telemetry flush --run <run id> # one run only
|
|
583
|
-
rcl telemetry flush --run <run id> --envelope-timeout-ms 120000
|
|
584
|
-
rcl telemetry rejected --run <run id> --json # inspect one retained original
|
|
585
|
-
rcl review owner/repo#7 --no-telemetry # keep this review on the machine
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
```yaml
|
|
589
|
-
# .review-council.yml
|
|
590
|
-
harness:
|
|
591
|
-
telemetry: full # off | envelope | findings | full (default)
|
|
592
|
-
parseFailures: false # send a parse-failed call's raw answer (scrubbed, 32 KB cap)
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
### `--attest`: attested reviews from the gate workflow
|
|
598
|
-
|
|
599
|
-
Only evidence recorded from the organization's own gate workflow on GitHub
|
|
600
|
-
Actions counts for the enforced gate (epic IO-12475, section 4.1). Inside
|
|
601
|
-
such a job — one that grants `id-token: write` — `rcl review owner/repo#N
|
|
602
|
-
--attest` asks the runner for the job's OIDC token with the Harness origin as
|
|
603
|
-
audience, exchanges it at `POST /api/v1/reviews/attest` for a **run-bound
|
|
604
|
-
credential** (`rbc_…`, thirty minutes, one rcl run id, valid while the
|
|
605
|
-
Actions run is in progress), and records the review under it: the envelope,
|
|
606
|
-
its artifacts, the model keys and the model stats all travel with that
|
|
607
|
-
credential and nothing else. Harness verifies the token, requires the
|
|
608
|
-
workflow file to be on the organization's gate allow-list at its default
|
|
609
|
-
branch, re-reads the pull request through its GitHub App and stores the run
|
|
610
|
-
only if the reviewed head is the pull request's current head and the PR is
|
|
611
|
-
not from a fork — the run is then `credential_kind: attested`.
|
|
612
|
-
|
|
613
|
-
`--attest` fails loudly, before any token is requested or any reviewer is
|
|
614
|
-
paid: outside Actions (no `ACTIONS_ID_TOKEN_REQUEST_URL` / `_TOKEN`), without
|
|
615
|
-
`HARNESS_API_URL`, off a pull request target, with a telemetry level other
|
|
616
|
-
than `full` (from `RCL_TELEMETRY` or the project config — an attested run
|
|
617
|
-
carries its full report), or when Harness refuses the exchange (the refusal
|
|
618
|
-
names the reason: `workflow_not_allowed`, `reviews_disabled`,
|
|
619
|
-
`run_not_in_progress`, …). It never falls back to `HARNESS_API_TOKEN` or the
|
|
620
|
-
stored login, it implies `--evidence-required`, and nothing recorded under the
|
|
621
|
-
run-bound credential is ever spooled — the credential does not outlive the
|
|
622
|
-
workflow run. A review that outlasts most of the credential's thirty minutes
|
|
623
|
-
mints it again for the same run id before delivery. Pair it with
|
|
624
|
-
`--expect-head-sha` so a moved pull request fails fast instead of being
|
|
625
|
-
refused at ingest.
|
|
626
|
-
|
|
627
|
-
If the completed envelope's POST becomes unavailable, delivery first reads a
|
|
628
|
-
restricted receipt for that same run with its still-live attested credential.
|
|
629
|
-
A matching receipt resumes artifact delivery without another POST; only an
|
|
630
|
-
explicit 404 permits replay of the exact serialized envelope. Recovery allows
|
|
631
|
-
at most three POSTs including the original, with an additional 20-second recovery
|
|
632
|
-
deadline bounded by credential expiry. Conflicts, rejected or unanswered receipts,
|
|
633
|
-
expiry and exhausted retries stop recovery. No reviewer is called again and no
|
|
634
|
-
ordinary credential is substituted. Failed delivery retains bounded, redacted
|
|
635
|
-
transport diagnostics, including the initial error cause, with the original
|
|
636
|
-
recovery evidence.
|
|
637
|
-
|
|
638
|
-
Operators recovering the gate's encrypted GitHub artifact must use the
|
|
639
|
-
[review evidence recovery runbook](https://github.com/allocator-one/rcl/blob/main/docs/review-evidence-recovery.md).
|
|
640
|
-
Recovery requires the separately held, version-mapped private key and does not
|
|
641
|
-
confer review or merge approval.
|
|
642
|
-
|
|
643
|
-
```yaml
|
|
644
|
-
# .github/workflows/review_gate.yml (dispatched by Harness for one pull request)
|
|
645
|
-
permissions:
|
|
646
|
-
id-token: write
|
|
647
|
-
contents: read
|
|
648
|
-
jobs:
|
|
649
|
-
review:
|
|
650
|
-
runs-on: ubuntu-latest
|
|
651
|
-
env:
|
|
652
|
-
HARNESS_API_URL: https://harness.infra.one
|
|
653
|
-
steps:
|
|
654
|
-
- run: npm i -g review-council
|
|
655
|
-
# Inputs reach the shell through the environment, never by expression
|
|
656
|
-
# interpolation into the command line.
|
|
657
|
-
- env:
|
|
658
|
-
REPO: ${{ inputs.repo }}
|
|
659
|
-
PR: ${{ inputs.pr }}
|
|
660
|
-
HEAD_SHA: ${{ inputs.head_sha }}
|
|
661
|
-
run: rcl review "$REPO#$PR" --attest --expect-head-sha "$HEAD_SHA" --ci
|
|
342
|
+
rcl roles list # all built-in roles
|
|
343
|
+
rcl roles show <name> # system prompt and details for a role
|
|
662
344
|
```
|
|
663
345
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
### `rcl evidence status` and `rcl evidence show`
|
|
667
|
-
|
|
668
|
-
What Harness holds — never the client's own claim. `rcl evidence status`
|
|
669
|
-
prints the gate status Harness computed for a pull request: its known head,
|
|
670
|
-
the advisory and enforced projections (status, the rounds behind them, the
|
|
671
|
-
actionable findings still open) and the merge decision once it merged. The
|
|
672
|
-
exit code is the contract the skills gate on:
|
|
673
|
-
|
|
674
|
-
| exit | meaning |
|
|
346
|
+
| Role | Focus |
|
|
675
347
|
| --- | --- |
|
|
676
|
-
|
|
|
677
|
-
|
|
|
678
|
-
|
|
|
679
|
-
|
|
|
680
|
-
|
|
681
|
-
`
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
`
|
|
688
|
-
|
|
689
|
-
unknown, and a displayed judgment does not assert current gate resolution.
|
|
690
|
-
|
|
691
|
-
Text preserves multiline explanations while scrubbing credential-shaped text
|
|
692
|
-
and terminal controls. `--json` preserves the API object's semantic values with
|
|
693
|
-
safe control-character escaping. Older servers may omit the optional evidence
|
|
694
|
-
and attribution fields. Local Markdown reports also show verifier explanations
|
|
695
|
-
for kept findings and the rendered below-threshold appendix; the appendix still
|
|
696
|
-
shows at most 20 findings and points to JSON for omitted entries.
|
|
697
|
-
|
|
698
|
-
The reads use the same credential rules as delivery — the stored `harness
|
|
699
|
-
login`, or `HARNESS_API_TOKEN` + `HARNESS_API_URL` in CI, the token sent to
|
|
700
|
-
its own host only — and need `reviews:read`. They do not depend on the
|
|
701
|
-
telemetry level: switching delivery off does not blind them.
|
|
702
|
-
Both commands send only their normal GET requests. They neither fetch an
|
|
703
|
-
artifact per finding nor flush pending retry deliveries, run reviewers or
|
|
704
|
-
record verdicts. Use `rcl telemetry flush` explicitly to retry queued delivery.
|
|
705
|
-
|
|
706
|
-
```bash
|
|
707
|
-
rcl evidence status # error: name the pull request
|
|
708
|
-
rcl evidence status 8524 # against the current checkout's origin remote
|
|
709
|
-
rcl evidence status '#8524' --enforced
|
|
710
|
-
rcl evidence status allocator-one/rcl#42 --json
|
|
711
|
-
rcl evidence status https://github.com/allocator-one/rcl/pull/42
|
|
712
|
-
rcl evidence show 01a08032-0838-76db-ade3-1990f6e54072
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
### `rcl evidence recover-run`
|
|
716
|
-
|
|
717
|
-
Recover one original completed asserted run without rerunning review, changing its
|
|
718
|
-
UUID or rewriting its JSON/Markdown artifacts. This is delivery only: it does not
|
|
719
|
-
admit a native round, record verdicts, change attempts/precision, flush unrelated
|
|
720
|
-
outbox entries or confer gate approval. Semantic claim splits are not part of this
|
|
721
|
-
command.
|
|
722
|
-
|
|
723
|
-
First prepare an exclusive manifest using explicit original source pins:
|
|
724
|
-
|
|
725
|
-
```sh
|
|
726
|
-
rcl evidence recover-run --preview --manifest original-run.json \
|
|
727
|
-
--run "$ORIGINAL_RUN_ID" --for-pr owner/repo#123 --head "$ORIGINAL_HEAD_SHA" \
|
|
728
|
-
--report-json /absolute/original/report.json --report-sha256 "$JSON_SHA256" \
|
|
729
|
-
--report-md /absolute/original/report.md --markdown-sha256 "$MARKDOWN_SHA256" \
|
|
730
|
-
--original-mode asserted --json
|
|
731
|
-
```
|
|
732
|
-
|
|
733
|
-
Markdown is optional; its path and digest must be supplied together. The report
|
|
734
|
-
must retain its complete modern header, explicit finding identities, and an exact
|
|
735
|
-
PR or PR-bound patch target. Original run UUID bytes are preserved; only generated operation IDs are canonical lowercase. Recovery
|
|
736
|
-
refuses other spellings instead of rewriting identity. Headerless imports, CI/attested/backfill originals,
|
|
737
|
-
unknown mode fields, missing sources and ambiguous bindings refuse. The explicit
|
|
738
|
-
asserted mode is an **operator assertion**, checked against the retained non-CI
|
|
739
|
-
runner metadata; it is not cryptographic proof of the original invocation. A
|
|
740
|
-
run-bound credential is never converted to a normal login.
|
|
741
|
-
|
|
742
|
-
Preview validates all local inputs before HTTP and writes only the explicitly
|
|
743
|
-
named manifest. Its formatted UTF-8 representation, including the final newline,
|
|
744
|
-
must fit within 8 MiB so apply and resume can read it. Oversized prepared evidence
|
|
745
|
-
refuses before HTTP; the complete manifest is checked again before publication.
|
|
746
|
-
It makes scoped authenticated GET requests, requires
|
|
747
|
-
`meta.original_report_recovery_version: 1` and evidence protocol 2, and binds the
|
|
748
|
-
host and server organization. An older or disabled server remains unsupported.
|
|
749
|
-
Inspect the manifest's exact envelope, source digests, transformations and retained
|
|
750
|
-
content limitations, then use the digest printed by preview:
|
|
751
|
-
|
|
752
|
-
```sh
|
|
753
|
-
rcl evidence recover-run --apply --manifest original-run.json \
|
|
754
|
-
--manifest-sha256 "$MANIFEST_SHA256" --json
|
|
755
|
-
|
|
756
|
-
# After interruption or an uncertain acknowledgment, reuse that same operation.
|
|
757
|
-
rcl evidence recover-run --resume --manifest original-run.json \
|
|
758
|
-
--manifest-sha256 "$MANIFEST_SHA256" --json
|
|
759
|
-
```
|
|
760
|
-
|
|
761
|
-
Apply starts an adjacent `original-run.json.journal` directory with append-only,
|
|
762
|
-
fsynced checkpoints before every remote write. Each checkpoint has an
|
|
763
|
-
8 MiB + 1 KiB read/write bound, retaining the manifest's full prose audit and
|
|
764
|
-
reserving space for the checkpoint wrapper.
|
|
765
|
-
Oversized checkpoints refuse before publication. Apply and resume require the
|
|
766
|
-
journal to be effective-user-owned mode 0700, on the supported local storage
|
|
767
|
-
listed below, with protected ancestors and no harmful or unknown ACL grants.
|
|
768
|
-
Apply checks the selected parent before creating the journal exclusively; resume
|
|
769
|
-
never creates missing state or repairs permissions. Each append checks the
|
|
770
|
-
selected journal's device/inode identity before writing. This detects replacement
|
|
771
|
-
between checkpoints; it does not claim protection against concurrent privileged
|
|
772
|
-
or same-user path manipulation. Resume requires that directory;
|
|
773
|
-
it never generates a replacement operation. A dedicated
|
|
774
|
-
`RCL_DATA_DIR/original-run-recovery-locks` directory serializes applies for the same
|
|
775
|
-
host/organization/run, including different manifest paths. Native accounting
|
|
776
|
-
locks and stores are not used. Locks with incomplete or unverifiable ownership
|
|
777
|
-
fail closed and require inspection; never delete a live lock.
|
|
778
|
-
|
|
779
|
-
The lock uses a unique registration per acquisition and automatically removes a
|
|
780
|
-
dead participant only when its PID is absent in the same kernel boot and PID
|
|
781
|
-
namespace. A reboot, foreign scope, PID reuse or uncertain liveness requires
|
|
782
|
-
inspection or a bounded retry; age alone never permits deletion. Legacy private
|
|
783
|
-
`.lock`/`.reclaim` state is refused, not migrated or bypassed. Empty `.bakery`
|
|
784
|
-
registries remain in place, and an interrupted unpublished `.tmp` is harmless.
|
|
785
|
-
Concurrent older private recovery clients are unsupported.
|
|
786
|
-
|
|
787
|
-
This protocol requires coherent ordinary local storage: local APFS/HFS with
|
|
788
|
-
ownership enabled on macOS, or ext2/3/4, tmpfs, XFS or Btrfs on Linux. Network,
|
|
789
|
-
FUSE, overlay and unknown filesystems are unsupported. macOS refuses an ambiguous
|
|
790
|
-
system mount listing, including an ambiguous entry for an unrelated mount. It
|
|
791
|
-
uses the existing directory's filesystem name and mountpoint from bounded
|
|
792
|
-
`df --libxo json` output, matched to one exact mount-table entry; firmlink or case
|
|
793
|
-
aliases never select an ancestor's flags. Unavailable structured inspection or
|
|
794
|
-
unmatched/ambiguous attribution refuses without a fallback. It
|
|
795
|
-
rejects ACL allow grants or unrecognized ACL output; restrictive deny-only ACLs
|
|
796
|
-
are allowed. The root must already be private (effective-user-owned
|
|
797
|
-
mode 0700), and ancestors must be protected against other users' writes, apart
|
|
798
|
-
from root-owned sticky temporary directories. Missing private directories are
|
|
799
|
-
created; existing permissions are never silently repaired. These checks do not
|
|
800
|
-
certify arbitrary filesystem implementations or protect against hostile code
|
|
801
|
-
running as the same user or a privileged administrator.
|
|
802
|
-
|
|
803
|
-
Every invocation rechecks source bytes and the reviewed manifest. Before retrying,
|
|
804
|
-
it reads and compares all immutable run header/settings/findings/calls/artifact
|
|
805
|
-
declarations, then fetches exact raw artifact bytes and verifies their digest and
|
|
806
|
-
size. A POST duplicate receipt or a stored-artifact flag alone is insufficient.
|
|
807
|
-
Lost POST/PUT acknowledgment can finish through exact reads; otherwise the same
|
|
808
|
-
journal remains incomplete. Changed bytes, organization, header, findings or calls
|
|
809
|
-
refuse. A torn last checkpoint is retained and bound into the next append; it is
|
|
810
|
-
never treated as acknowledgment. Preserve the manifest, journal and sources until
|
|
811
|
-
independent reconciliation is complete.
|
|
812
|
-
|
|
813
|
-
Only unpaired UTF-16 units in actual finding `title`, `description` or
|
|
814
|
-
`suggestedFix` prose receive a derived wire spelling: visible ASCII `\uD800`,
|
|
815
|
-
using uppercase hex. Valid surrogate pairs and existing literal backslash-u text
|
|
816
|
-
remain unchanged. Each transformation records the original JSON path, code-unit
|
|
817
|
-
index and byte offset. Keys, identifiers, descriptors and structural fields cannot
|
|
818
|
-
receive that transformation. Existing producer `[redacted]` literals in finding
|
|
819
|
-
prose stay unchanged and are listed as retained-content limitations; markers in
|
|
820
|
-
protected bindings, or any newly required secret redaction, refuse. Markdown
|
|
821
|
-
requiring redaction is unsupported. No fresh-report normalizer or backfill UUID
|
|
822
|
-
is applied to the original.
|
|
823
|
-
|
|
824
|
-
An original that contains an escaped C0 control or literal DEL in that same
|
|
825
|
-
finding prose is unsupported by default. When the retained report must be
|
|
826
|
-
represented, select the explicit, versioned mode during preview:
|
|
827
|
-
|
|
828
|
-
```sh
|
|
829
|
-
rcl evidence recover-run --preview --manifest original-run.json \
|
|
830
|
-
--run "$ORIGINAL_RUN_ID" --for-pr owner/repo#123 --head "$ORIGINAL_HEAD_SHA" \
|
|
831
|
-
--report-json /absolute/original/report.json --report-sha256 "$JSON_SHA256" \
|
|
832
|
-
--original-mode asserted --original-prose control-code-units-v1 --json
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
This selection never rewrites the retained artifact or its digest. It projects
|
|
836
|
-
only allowed finding-prose controls to visible uppercase `\uXXXX` transport text
|
|
837
|
-
and records a version-1 `control_code_unit` transformation with the source path,
|
|
838
|
-
code-unit offset and original UTF-8 byte offset. Short JSON escapes
|
|
839
|
-
(`\b`, `\f`), `\uXXXX` escapes and literal DEL are covered. Literal tab, LF
|
|
840
|
-
and CR are valid JSON prose and remain literal; they are not transformed. Raw
|
|
841
|
-
unescaped C0 is invalid JSON; controls in keys, descriptors, identifiers,
|
|
842
|
-
locations or other structural fields refuse. Existing surrogate records keep
|
|
843
|
-
their prior shape.
|
|
844
|
-
|
|
845
|
-
The mode is intentionally unavailable against older servers. Preview requires
|
|
846
|
-
the usual recovery/evidence metadata **and**
|
|
847
|
-
`meta.original_prose_representation_version: 1`; without it, it makes the scoped
|
|
848
|
-
capability GET but writes no manifest and sends no delivery request. Apply and
|
|
849
|
-
resume use the selected, manifest-pinned representation and repeat that check.
|
|
850
|
-
Inspect the visible projection and transformation records before applying. A
|
|
851
|
-
successful delivery still does not repair native accounting or establish a fresh
|
|
852
|
-
review gate.
|
|
853
|
-
|
|
854
|
-
Existing transport scrubbing, limits, call summaries and duration rounding remain
|
|
855
|
-
explicitly recorded derivations. Two valid reversed integer coordinates may use
|
|
856
|
-
`report_projection` provenance bound to the original coordinates and JSON digest;
|
|
857
|
-
original finding identities are never recomputed from the normalized interval.
|
|
858
|
-
Unsupported coordinate types refuse rather than being guessed.
|
|
859
|
-
|
|
860
|
-
Exit codes: `0` means preview prepared or delivery independently verified; `2`
|
|
861
|
-
means invalid/unavailable local selection; `3` means remote capability/read/delivery
|
|
862
|
-
unanswered; `4` means conflicting destination, evidence or journal bindings; `5` means local durable
|
|
863
|
-
journal/lock persistence failed. JSON diagnostics include the stage and next step.
|
|
864
|
-
Even successful delivery does not establish current-head review freshness,
|
|
865
|
-
convergence, attestation or historical accounting repair.
|
|
866
|
-
|
|
867
|
-
### `rcl evidence recover-finding`
|
|
868
|
-
|
|
869
|
-
Recover one recorded finding whose report identity collided, using its retained
|
|
870
|
-
native convergence identity. Preview is the default; `--submit` explicitly
|
|
871
|
-
posts one attributed `finding_identity_corrected` event. This unpaid command
|
|
872
|
-
never calls reviewers, claims an attempt, changes a round or verdict, updates
|
|
873
|
-
precision accounting, flushes/spools an outbox, or rewrites native history.
|
|
874
|
-
|
|
875
|
-
```bash
|
|
876
|
-
rcl evidence recover-finding --target "$TARGET" --run "$RUN_ID" \
|
|
877
|
-
--report-sha256 "$ORIGINAL_REPORT_SHA256" --finding-ref f002 \
|
|
878
|
-
--identity "$NATIVE_IDENTITY" --for-pr example/project#42
|
|
879
|
-
# Inspect the preview, then repeat with --submit if authorized.
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
Run it in the checkout holding the retained `.git/rcl-converge-runs` state.
|
|
883
|
-
All selectors are mandatory. The command reads the server run and checks its
|
|
884
|
-
ID, original report digest, repository/PR, convergence target and round. The
|
|
885
|
-
explicit native identity must match the selected ref's exact file, category
|
|
886
|
-
and line span. Its latest sighting, verdict and native round-to-run binding
|
|
887
|
-
must belong to that same recorded round. Missing or ambiguous evidence is an
|
|
888
|
-
error, never a reason to reconstruct state, reset counters or rerun review.
|
|
889
|
-
|
|
890
|
-
The event includes the digest of the exact retained state bytes and the minimal
|
|
891
|
-
native identity/verdict assertion, not private verdict reasons. Harness must
|
|
892
|
-
already hold the corresponding canonical verdict on that same run and round;
|
|
893
|
-
the command does not create one. Harness validates the bindings, but trusts the
|
|
894
|
-
authenticated actor's native mapping assertion. Neither the command nor the
|
|
895
|
-
server claims to have retrieved or verified the original report bytes. Normal
|
|
896
|
-
transport scrubbing applies; if redaction or truncation would change an exact
|
|
897
|
-
binding, both preview and submission refuse it rather than print the raw value
|
|
898
|
-
or silently rebind the evidence.
|
|
899
|
-
|
|
900
|
-
Uses the normal Harness credential rules, requiring `reviews:read` for preview
|
|
901
|
-
and also `reviews:write` for submission. Requires backend support for the new
|
|
902
|
-
event; an older backend rejects it without changing history. Conflicts and
|
|
903
|
-
network errors fail visibly, without automatic retries or spooling. An
|
|
904
|
-
acknowledgment (exit 0) is not a convergence verdict; separately inspect
|
|
905
|
-
`rcl evidence status` when authorized. Originals and sibling sightings remain
|
|
906
|
-
unchanged, and the correction is not inherited by another run. A correction can
|
|
907
|
-
reopen a previously suppressed critical finding if its canonical verdict is not
|
|
908
|
-
critical. A `fixed` correction is audit-only for that run and does not clear
|
|
909
|
-
`fixes_pending`.
|
|
910
|
-
|
|
911
|
-
### `rcl evidence retriage-finding`
|
|
912
|
-
|
|
913
|
-
Record a **new explicit dismissal** for one existing finding at its actual
|
|
914
|
-
recorded severity. This repairs a historical grouped-severity dismissal without
|
|
915
|
-
replaying the report or guessing a native identity after spans have drifted.
|
|
916
|
-
It is not an automatic upgrade of the old verdict or a gate waiver.
|
|
917
|
-
|
|
918
|
-
```bash
|
|
919
|
-
rcl evidence retriage-finding --target "$TARGET" --run "$RUN_ID" \
|
|
920
|
-
--report-sha256 "$ORIGINAL_REPORT_SHA256" --finding-ref f002 \
|
|
921
|
-
--for-pr example/project#42 --reason-file ./retriage-reason.txt
|
|
922
|
-
# Inspect the preview and source-backed reason; repeat with --submit if authorized.
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
All selectors and the UTF-8 reason file are required. The nonblank reason is
|
|
926
|
-
limited to 2000 characters; malformed UTF-8 is refused. The command reads the
|
|
927
|
-
run, checks its PR, head and stored report metadata, and requires one exact
|
|
928
|
-
finding ref with a unique `report:<run-id>:<key>` identity (RCL 3.3+). A native
|
|
929
|
-
convergence run must match the selected target and carry a positive round. A
|
|
930
|
-
standalone gate run without convergence metadata is accepted only when Harness
|
|
931
|
-
records it as an attested, current-head, same-repository CI review. Its PR, run,
|
|
932
|
-
report digest and finding ref provide the server binding; the selected target
|
|
933
|
-
remains the event's informational label. The required event round is `1` as a
|
|
934
|
-
wire-protocol value only, not a claim that the attested review participated in
|
|
935
|
-
native convergence. Legacy unqualified or
|
|
936
|
-
colliding keys are refused, because a verdict on those keys could affect an
|
|
937
|
-
unrelated sighting. The digest is compared with the stored artifact metadata;
|
|
938
|
-
the command does not retrieve or claim to verify the original report bytes.
|
|
939
|
-
It does not need or read native convergence state. `recover-finding` retains
|
|
940
|
-
its separate exact-span/native-verdict checks unchanged.
|
|
941
|
-
|
|
942
|
-
Preview performs only the run read and labels the standalone-attested transport
|
|
943
|
-
round when applicable. `--submit` appends one fresh, authenticated
|
|
944
|
-
`verdicts_recorded` event under the original report key, on the selected run,
|
|
945
|
-
with the reason and recorded severity. No existing report, verdict,
|
|
946
|
-
mapping, attempt count, model statistics or native file is rewritten. No
|
|
947
|
-
reviewers run, and no outbox is flushed or spooled. Scrubbing that would alter
|
|
948
|
-
the selected evidence or reason causes refusal before submission. The existing
|
|
949
|
-
Harness API and its critical-dismissal check remain unchanged.
|
|
950
|
-
|
|
951
|
-
Uses the normal Harness credential rules (`reviews:read`, plus `reviews:write`
|
|
952
|
-
to submit). A refusal or uncertain response fails visibly, without automatic
|
|
953
|
-
retry. Each submission is a fresh attributed judgment, not an idempotent replay
|
|
954
|
-
of an old event; inspect server evidence before retrying an uncertain write.
|
|
955
|
-
Exit 0 means preview succeeded or exactly one new insertion was acknowledged, **not** that the
|
|
956
|
-
gate converged. Independently run `rcl evidence status` for the exact PR.
|
|
957
|
-
|
|
958
|
-
### `--for-pr` on a patch-file review
|
|
959
|
-
|
|
960
|
-
A patch-file review (`rcl review changes.patch`) carries no repository or pull
|
|
961
|
-
request, so Harness records it as a `patch` run it cannot verify or count for
|
|
962
|
-
any gate. `--for-pr owner/repo#N` (or a pull request URL) names the pull
|
|
963
|
-
request the patch was taken from (`RCL_FOR_PR` in the environment does the
|
|
964
|
-
same for patch files): the run is bound to that pull request and its
|
|
965
|
-
`--head-sha` — required with the flag — is verified against the pull
|
|
966
|
-
request's head. Owner and repository are lower-cased, as GitHub reads them. Counting the round for that pull request's
|
|
967
|
-
gate is the server half (IO-12585); until it lands, `rcl evidence status`
|
|
968
|
-
still reads `stale`/`none` for patch-file loops. The flag is refused on PR and
|
|
969
|
-
git-mode targets, which name their own pull request or checkout. `rcl-converge` passes `--converge-target`, `--round`
|
|
970
|
-
and `--attempt` on every round and adds `--for-pr` when a pull request loop
|
|
971
|
-
reviews a patch file taken from the pull request (a pull request target names
|
|
972
|
-
its own). A converge
|
|
973
|
-
target of the `owner/repo#N` form attributes the run the same way; a slug such
|
|
974
|
-
as `rcl-7` does not.
|
|
348
|
+
| `general` | Comprehensive review covering all dimensions |
|
|
349
|
+
| `security-auditor` | Auth, injection, XSS, CSRF, IDOR, and sensitive data exposure |
|
|
350
|
+
| `performance-engineer` | N+1 queries, caching, algorithmic complexity, and memory efficiency |
|
|
351
|
+
| `api-design` | API contracts, breaking changes, REST/gRPC conventions |
|
|
352
|
+
| `test-coverage` | Missing tests, edge cases, flawed test logic |
|
|
353
|
+
| `dx-critic` | Readability, naming, documentation, and developer ergonomics |
|
|
354
|
+
| `architecture` | Module boundaries, coupling, and architectural patterns |
|
|
355
|
+
| `bug-hunter` | Logic errors, null paths, race conditions, off-by-one |
|
|
356
|
+
| `accessibility-auditor` | WCAG compliance, ARIA roles, keyboard navigation |
|
|
357
|
+
| `spec-compliance` | Checks the implementation against a spec or plan file |
|
|
358
|
+
| `regression-hunter` | Changed defaults, weakened guards, and lost behavior |
|
|
359
|
+
| `dependency-hygiene` | Unnecessary dependencies, external requests, and privacy leaks |
|
|
360
|
+
| `edge-case-hunter` | Boundary values, unusual inputs, and failure paths |
|
|
975
361
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
362
|
+
By default every role runs except `spec-compliance`, which runs only when a
|
|
363
|
+
spec is supplied. `general` runs on each primary model; specialist roles are
|
|
364
|
+
spread round-robin across primary and secondary models. The default council
|
|
365
|
+
therefore schedules 13 reviewer seats, or 14 when a spec enables
|
|
366
|
+
`spec-compliance`. Seats on the primary models (Opus 5.5 and Sol) form the
|
|
367
|
+
blocking lane that reviewer health and quorum count; Gemini receives specialist
|
|
368
|
+
seats only, in the secondary lane, whose results keep their findings but do not
|
|
369
|
+
count toward quorum. The async reviewer and the verifier are separate lanes.
|
|
370
|
+
|
|
371
|
+
The first repository rules file found in the working directory — `AGENTS.md`,
|
|
372
|
+
`CLAUDE.md`, `CONTRIBUTING.md`, `.github/CONTRIBUTING.md`, `DEVELOPMENT.md` or
|
|
373
|
+
`docs/CONTRIBUTING.md`, in that order — is supplied to every reviewer as shared
|
|
374
|
+
context.
|
|
375
|
+
`project-rules` and `dead-code` are no longer built-in roles; remove them from
|
|
376
|
+
explicit role lists (unknown roles are skipped with a warning) unless you define
|
|
377
|
+
a custom role with that name.
|
|
980
378
|
|
|
981
379
|
### `rcl models`
|
|
982
380
|
|
|
983
381
|
The tool's own memory of which reviewers earn their seat. Every reviewer call
|
|
984
382
|
and every `converge-verdict` outcome accrues in a cross-run store at `~/.rcl`
|
|
985
|
-
(`RCL_DATA_DIR` overrides
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
in consensus gating, so persistently noisy models lose gating power
|
|
383
|
+
(`RCL_DATA_DIR` overrides). `rcl models` prints, per model over a trailing
|
|
384
|
+
90-day window: triage precision (the share of its supported findings the
|
|
385
|
+
convergence loop verified and fixed rather than dismissed), triage volume, call
|
|
386
|
+
volume, dead-call rate, p50 latency — and the consensus **weight** the model
|
|
387
|
+
earns: `0.5 + precision`, clamped to [0.5, 1.5], neutral (1) below 20 triaged
|
|
388
|
+
outcomes. Weights scale each model's consensus vote in report confidence and in
|
|
389
|
+
consensus gating, so persistently noisy models lose gating power
|
|
993
390
|
automatically; the applied weights are visible per finding
|
|
994
391
|
(`consensus.weightedScore` / `consensus.modelWeights`) and per run
|
|
995
392
|
(`stats.modelWeights`) in the report JSON.
|
|
996
393
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
Since 3.1 the table merges the organization's window from Harness
|
|
1006
|
-
(`GET /api/v1/reviews/model-stats`, the server-side `rcl models` over every run
|
|
1007
|
-
the org recorded, backfilled history included) with this machine's store: for a
|
|
1008
|
-
model the server holds at least 20 outcomes for, the server's weight is used
|
|
1009
|
-
(`source: server`); below that the local store decides (`local`); a model
|
|
1010
|
-
neither knows enough about keeps the neutral weight (`neutral`). Reviews weight
|
|
1011
|
-
consensus the same way, asking the server with a three-second bound and falling
|
|
1012
|
-
back to the local store when it cannot answer. `--local` shows this machine's
|
|
1013
|
-
view alone; `--json` carries `server` (host, window, rows) and `weights` with
|
|
1014
|
-
their `source`.
|
|
1015
|
-
|
|
1016
|
-
```bash
|
|
1017
|
-
rcl models # org-wide where Harness has enough history, local otherwise
|
|
1018
|
-
rcl models show --local # this machine's store only
|
|
1019
|
-
rcl models show --window 30 --json
|
|
1020
|
-
```
|
|
1021
|
-
|
|
1022
|
-
### `rcl telemetry backfill`
|
|
1023
|
-
|
|
1024
|
-
Recovered history becomes day-one evidence on Harness. `rcl telemetry backfill
|
|
1025
|
-
--from <dir> --repo <owner/repo>` reads the pre-3.0 `rcl-report-*.json`
|
|
1026
|
-
reports and `rcl-converge-*-ledger.md` ledgers in a directory (the same layout
|
|
1027
|
-
`rcl models seed` reads) and posts each report as a run with `provenance:
|
|
1028
|
-
backfill`: a synthesized header bound to the named repository (target `patch`,
|
|
1029
|
-
the report bytes as the digest, a runner claim naming this command), the
|
|
1030
|
-
report's findings with their stable identities, its reviewer calls, and the
|
|
1031
|
-
report files as artifacts. Ledger bullets matched to a round's findings become
|
|
1032
|
-
`verdicts_recorded` events. Run ids are UUIDv5 of `(host, repo, sha256 of the
|
|
1033
|
-
report)` and event ids derive from them, so running the backfill twice reports
|
|
1034
|
-
the second run as `0 new` — nothing is duplicated. Backfilled runs count for
|
|
1035
|
-
model stats and analytics and never enter a gate decision.
|
|
394
|
+
With Harness evidence configured, the table merges the organization's window
|
|
395
|
+
from Harness (`GET /api/v1/reviews/model-stats`, computed over every run the
|
|
396
|
+
organization recorded) with this machine's store: for a model the server holds
|
|
397
|
+
at least 20 outcomes for, the server's weight is used (`source: server`); below
|
|
398
|
+
that the local store decides (`local`); a model neither knows enough about
|
|
399
|
+
keeps the neutral weight (`neutral`). Reviews weight consensus the same way,
|
|
400
|
+
asking the server with a three-second bound and falling back to the local store
|
|
401
|
+
when it cannot answer.
|
|
1036
402
|
|
|
1037
403
|
```bash
|
|
1038
|
-
rcl
|
|
1039
|
-
rcl
|
|
404
|
+
rcl models # org-wide where Harness has enough history, local otherwise
|
|
405
|
+
rcl models show --window 30 --json # `server` (host, window, rows) and `weights` with their `source`
|
|
406
|
+
rcl models show --local # this machine's store only
|
|
407
|
+
rcl models seed --from ~/recovered-rcl-artifacts # backfill the local store from reports and converge ledgers
|
|
1040
408
|
```
|
|
1041
409
|
|
|
1042
|
-
### `rcl
|
|
410
|
+
### `rcl evidence` and `rcl telemetry`
|
|
1043
411
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
explicit step. No reviewer is rerun, no triage events are invented, and retry
|
|
1048
|
-
queues and source reports remain untouched.
|
|
412
|
+
These commands read and deliver review evidence on Harness. Details, exit codes
|
|
413
|
+
and the recovery runbooks are in
|
|
414
|
+
[Telemetry and evidence](https://github.com/allocator-one/rcl/blob/main/docs/telemetry-and-evidence.md).
|
|
1049
415
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
rcl
|
|
416
|
+
| Command | Purpose |
|
|
417
|
+
| --- | --- |
|
|
418
|
+
| `rcl evidence status <pr> [--enforced]` | Gate status Harness computed for a pull request; exit 0 only when the judged projection is `converged` |
|
|
419
|
+
| `rcl evidence show <run-id>` | One recorded run: header, reviewer health, artifacts, findings with identity, gating reason and verdict |
|
|
420
|
+
| `rcl evidence recover-run` | Preview, apply or resume delivery of one original asserted run |
|
|
421
|
+
| `rcl evidence recover-finding` | Preview or submit a correction of one recorded finding identity |
|
|
422
|
+
| `rcl evidence retriage-finding` | Preview or submit a fresh dismissal of one finding at its recorded severity |
|
|
423
|
+
| `rcl telemetry status` | Telemetry level, credential source and spooled deliveries |
|
|
424
|
+
| `rcl telemetry flush [--run <id>]` | Deliver spooled evidence |
|
|
425
|
+
| `rcl telemetry rejected` | Inspect retained rejected evidence without delivering it |
|
|
426
|
+
| `rcl telemetry recover-reviewer --target <t> --run <id>` | Redeliver one exact retained terminal reviewer report without restarting reviewers or verification |
|
|
427
|
+
| `rcl telemetry backfill` | Post recovered pre-3.0 reports and ledgers as backfill evidence |
|
|
428
|
+
| `rcl telemetry recover-refutations` | Discover original verifier explanations and write a reviewed recovery manifest |
|
|
429
|
+
|
|
430
|
+
### Convergence commands
|
|
431
|
+
|
|
432
|
+
Convergence-loop state lives under the repository's common Git directory. The
|
|
433
|
+
commands are documented in
|
|
434
|
+
[Convergence and recovery](https://github.com/allocator-one/rcl/blob/main/docs/convergence.md).
|
|
435
|
+
|
|
436
|
+
| Command | Purpose |
|
|
437
|
+
| --- | --- |
|
|
438
|
+
| `rcl converge-report` | Dedupe a round report against earlier rounds, enforce the round cap, classify findings (`new` / `repeat` / `suppressed` / `regating`); refuses inconclusive reviewer health |
|
|
439
|
+
| `rcl converge-verdict` | Record fixed/dismissed verdicts and report the round's resolution |
|
|
440
|
+
| `rcl converge-stale` | Audited disposition of a healthy report that became stale before admission |
|
|
441
|
+
| `rcl converge-gap` | Audited record of one evidenced missing terminal report |
|
|
442
|
+
| `rcl converge-rejected` | Audited disposition of a report rejected locally before delivery |
|
|
443
|
+
| `rcl converge-attempt` | Legacy attempt accounting; guarded launches claim their own attempts |
|
|
1053
444
|
|
|
1054
|
-
|
|
1055
|
-
rcl telemetry recover-refutations --root ~/Development --root /tmp --manifest recovery.json
|
|
445
|
+
---
|
|
1056
446
|
|
|
1057
|
-
|
|
1058
|
-
rcl telemetry recover-refutations --manifest recovery.json --apply --output outcome.json
|
|
1059
|
-
```
|
|
447
|
+
## Configuration
|
|
1060
448
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
Default discovery covers `~/Development`, `/tmp`, `/private/tmp`, the configured
|
|
1069
|
-
OS temporary directory and `RCL_DATA_DIR` (otherwise `~/.rcl`). It also inspects
|
|
1070
|
-
registered Git worktrees/common directories, RCL output and outbox directories,
|
|
1071
|
-
and explicit JSON report references in retained ledgers and task metadata,
|
|
1072
|
-
including references beyond the initial roots. Add `--root` for other retained
|
|
1073
|
-
cache/task locations. Only bounded candidate files are read; symlinks, changing
|
|
1074
|
-
files, invalid UTF-8 and files larger than 25 MiB are rejected. Incomplete,
|
|
1075
|
-
missing and inaccessible sources stay in the coverage report. Re-inventory after
|
|
1076
|
-
active reviews finish and record a final discovery cutoff.
|
|
1077
|
-
|
|
1078
|
-
Identical report bytes collapse to one SHA-256 entry with all discovered file
|
|
1079
|
-
locations. The manifest retains positional finding refs, original identities,
|
|
1080
|
-
normalized model/notes and source bindings. Missing original notes remain explicit.
|
|
1081
|
-
Legacy repository ownership must be proven by a registered source worktree or a
|
|
1082
|
-
retained ledger in that worktree; ambiguous ownership is unresolved. A directory
|
|
1083
|
-
marked `SYNTHETIC_TEST_ONLY`, or a repeated `--exclude-sha256 <digest>`, explicitly
|
|
1084
|
-
excludes synthetic evidence and all copies with that digest. Missing reference
|
|
1085
|
-
paths are counted separately from missing reports; a basename match is not proof.
|
|
1086
|
-
|
|
1087
|
-
| Planned action | Application |
|
|
1088
|
-
| --- | --- |
|
|
1089
|
-
| `upload_and_recover` | Upload only the absent original artifact matching the recorded run's declaration, then select that run for server recovery. |
|
|
1090
|
-
| `recover` | Select an existing run for the server's validated, append-only recovery operation. RCL does not patch findings. |
|
|
1091
|
-
| `import_history` | Import missing evidence once under a deterministic historical ID; preserve original timing/target and explicit original-run/digest binding for modern reports. |
|
|
1092
|
-
| `already_present` | Read and confirm the recorded evidence; no write. |
|
|
1093
|
-
| `skip`, `conflict`, `unavailable` | Preserve the disposition for resolution; no write. |
|
|
1094
|
-
|
|
1095
|
-
Apply rechecks source bytes and server bindings. It can use a retained,
|
|
1096
|
-
digest-verified copy if another location disappeared. It never replaces an
|
|
1097
|
-
existing run or escalates an existing-run selection into a new historical import.
|
|
1098
|
-
For legacy reports, the established UUID derives from lowercase host (including
|
|
1099
|
-
port), repository and **original** report digest. Its scrubbed upload may have a
|
|
1100
|
-
different digest, recorded in the artifact declaration. Modern originals needing
|
|
1101
|
-
redaction are never uploaded under their original digest. When that exact
|
|
1102
|
-
artifact is already stored in Harness, it can still supply server recovery without
|
|
1103
|
-
another upload. Unsafe, unsupported or conflicting sources require resolution.
|
|
1104
|
-
|
|
1105
|
-
An apply outcome includes `server_recovery_run_ids`. An authorized operator passes
|
|
1106
|
-
those IDs to the released, bounded Harness recovery operation documented in
|
|
1107
|
-
[`harness_review_verification.md`](https://github.com/allocator-one/allocator-one/blob/main/docs/ops/harness_review_verification.md),
|
|
1108
|
-
previews the selection, applies it with explicit operator/operation attribution,
|
|
1109
|
-
and retains its results. Rerun the reviewed manifest to verify the common API
|
|
1110
|
-
projection afterwards. Repeat application is resumable and idempotent, including
|
|
1111
|
-
when a delivery receipt is lost; the source, queues and manifest are never deleted.
|
|
1112
|
-
Outcome `writes` counts acknowledged creations, so a lost receipt can leave a
|
|
1113
|
-
confirmed stored result without a creation count. The report dispositions and
|
|
1114
|
-
readback determine completion.
|
|
1115
|
-
|
|
1116
|
-
Manifest/output paths must be new and are published atomically with mode `0600`.
|
|
1117
|
-
Without `--output`, apply uses a unique outcome filename beside the manifest.
|
|
1118
|
-
Exit `0` means a plan/inventory was written, or apply reconciled its selected
|
|
1119
|
-
reports; `1` means apply still needs server recovery or source/conflict resolution;
|
|
1120
|
-
`2` means the command could not validate or perform the operation. Discovery
|
|
1121
|
-
issues and absent original notes still need explicit reconciliation even with
|
|
1122
|
-
exit `0`. Stopping and keeping the manifest is the rollback for an interrupted
|
|
1123
|
-
operation: do not delete historical records, rewrite original evidence, or flush
|
|
1124
|
-
queued live reviews as a recovery shortcut.
|
|
1125
|
-
|
|
1126
|
-
## Config File
|
|
1127
|
-
|
|
1128
|
-
Place `.review-council.yml` in your project root and run `rcl` from there. rcl looks only in the current working directory, not in parent directories. Use `--config <path>` for a file elsewhere. All fields are optional.
|
|
449
|
+
Place `.review-council.yml` (or `.review-council.yaml` / `.review-council.json`)
|
|
450
|
+
in the directory you run `rcl` from. rcl looks only in the current working
|
|
451
|
+
directory, not in parent directories; use `--config <path>` for a file
|
|
452
|
+
elsewhere. Executable JavaScript config is never discovered: rcl often runs in
|
|
453
|
+
untrusted checkouts with provider keys in the environment. All fields are
|
|
454
|
+
optional, and a config file that fails validation stops the review instead of
|
|
455
|
+
falling back to defaults.
|
|
1129
456
|
|
|
1130
457
|
```yaml
|
|
1131
|
-
# Blocking council (provider-prefixed names)
|
|
1132
|
-
# Shown here: the
|
|
1133
|
-
#
|
|
458
|
+
# Blocking council (provider-prefixed names); every round waits for these.
|
|
459
|
+
# Shown here: the defaults. Keep slow or aggregator-routed models out of this
|
|
460
|
+
# list; give them an async seat instead.
|
|
1134
461
|
models:
|
|
1135
462
|
- anthropic/claude-opus-5-5
|
|
1136
463
|
- openai/gpt-6-sol
|
|
@@ -1139,30 +466,24 @@ models:
|
|
|
1139
466
|
secondaryModels:
|
|
1140
467
|
- google/gemini-3.8-flash
|
|
1141
468
|
|
|
1142
|
-
# Async bonus reviewers
|
|
469
|
+
# Async bonus reviewers, fired with each round and never awaited. Results that
|
|
1143
470
|
# have arrived by the next round of the same target are merged into that
|
|
1144
471
|
# round's dedup and marked `async` in the report JSON.
|
|
1145
|
-
# Any model on https://openrouter.ai works — keep the vendor segment after the prefix.
|
|
1146
472
|
asyncModels:
|
|
1147
473
|
- openrouter/moonshotai/kimi-k3
|
|
1148
474
|
|
|
1149
|
-
#
|
|
475
|
+
# Roles to run (default: all built-in and custom roles; spec-compliance only with a spec)
|
|
1150
476
|
roles:
|
|
477
|
+
- general
|
|
1151
478
|
- security-auditor
|
|
1152
479
|
- bug-hunter
|
|
1153
|
-
- test-coverage
|
|
1154
|
-
|
|
1155
|
-
# Or pin explicit model:role pairs
|
|
1156
|
-
reviewers:
|
|
1157
|
-
- model: anthropic/claude-opus-5-5
|
|
1158
|
-
role: security-auditor
|
|
1159
|
-
- model: openai/gpt-6-sol
|
|
1160
|
-
role: bug-hunter
|
|
1161
480
|
|
|
1162
|
-
# Custom role
|
|
481
|
+
# Custom roles: a new role, or an override of a built-in with the same name
|
|
1163
482
|
customRoles:
|
|
1164
483
|
- name: my-style-guide
|
|
1165
|
-
focus: [best-practices]
|
|
484
|
+
focus: [best-practices] # categories this role specializes in
|
|
485
|
+
severityBias:
|
|
486
|
+
best-practices: 1.2 # >1: lean more severe; <1: lean less severe
|
|
1166
487
|
systemPrompt: |
|
|
1167
488
|
Enforce our team style guide. Flag any deviation from snake_case
|
|
1168
489
|
variable names and require docstrings on all public functions.
|
|
@@ -1174,242 +495,355 @@ thresholds:
|
|
|
1174
495
|
dedupeLineWindow: 5 # lines within which findings are merged
|
|
1175
496
|
jaccardThreshold: 0.3 # weighted title+description similarity threshold for dedup
|
|
1176
497
|
|
|
1177
|
-
#
|
|
1178
|
-
# A finding gates when multi-model, critical, or confirmed with source evidence by the
|
|
1179
|
-
# verifier; refuted or insufficient-evidence single-model claims stay visible but
|
|
1180
|
-
# stop blocking, and so does a claim the pass could not check (verdict
|
|
1181
|
-
# unavailable): verification promotes nothing it did not check. Report
|
|
1182
|
-
# JSON marks every finding with gating.reason
|
|
1183
|
-
# (consensus | critical | verified | none).
|
|
498
|
+
# Which findings block convergence and CI
|
|
1184
499
|
gating:
|
|
1185
|
-
mode: verified-consensus # or all-findings (
|
|
500
|
+
mode: verified-consensus # or all-findings (severity alone decides)
|
|
1186
501
|
minModels: 2 # distinct models for consensus gating
|
|
1187
|
-
verificationModel: openai/gpt-6-astra # direct-API only
|
|
1188
|
-
verificationReasoningEffort: high
|
|
1189
|
-
verificationPassTimeout: 600000
|
|
502
|
+
verificationModel: openai/gpt-6-astra # direct-API models only
|
|
503
|
+
verificationReasoningEffort: high # OpenAI verifier only; separate from reviewer effort
|
|
504
|
+
verificationPassTimeout: 600000 # ms for the complete verification queue
|
|
1190
505
|
|
|
1191
|
-
# Output defaults
|
|
1192
506
|
output:
|
|
1193
|
-
markdown: true
|
|
1194
|
-
markdownPath: review-report.md
|
|
1195
507
|
belowThresholdAppendix: true # false drops below-threshold findings outright
|
|
1196
508
|
|
|
1197
509
|
# Concurrency and reliability
|
|
1198
510
|
concurrency: 9 # maximum simultaneous blocking reviewer calls per process
|
|
1199
|
-
# set to 6 to retain the previous limit
|
|
1200
511
|
providerConcurrency: # provider admission caps, applied in addition to concurrency
|
|
1201
|
-
anthropic: 2 # default
|
|
1202
|
-
timeout: 540000 # ms per blocking model call
|
|
1203
|
-
asyncTimeout: 900000 # ms per async-lane call
|
|
1204
|
-
# quorumFraction: 0.75 #
|
|
1205
|
-
# on every chunk; all stragglers can be canceled, including core models.
|
|
1206
|
-
# Secondary successes never count; secondary calls still
|
|
1207
|
-
# running at closure are canceled. Also raises the report's
|
|
1208
|
-
# blocking-health requirement.
|
|
1209
|
-
# Default: exactly 2/3 — leave unset for that; 1 disables
|
|
1210
|
-
# closure and waits for every call.
|
|
512
|
+
anthropic: 2 # default
|
|
513
|
+
timeout: 540000 # ms per blocking model call
|
|
514
|
+
asyncTimeout: 900000 # ms per async-lane call
|
|
515
|
+
# quorumFraction: 0.75 # see below; default exactly 2/3
|
|
1211
516
|
maxRetries: 3
|
|
1212
517
|
|
|
1213
|
-
# Reasoning
|
|
1214
|
-
# low | medium | high — default low (supported by the default Kimi K3).
|
|
1215
|
-
# Check the selected model's supported levels before overriding. Unbounded reasoning makes these
|
|
1216
|
-
# models spend the whole completion budget thinking before they answer;
|
|
1217
|
-
# select a supported higher level when evaluation justifies deeper review.
|
|
518
|
+
# Reasoning effort for OpenRouter reviewers: low | medium | high (default low)
|
|
1218
519
|
reasoningEffort: low
|
|
1219
520
|
|
|
1220
|
-
# Context files
|
|
521
|
+
# Context files attached to every review, and the spec for spec-compliance
|
|
1221
522
|
context:
|
|
1222
523
|
- ARCHITECTURE.md
|
|
1223
524
|
- docs/api.md
|
|
1224
|
-
|
|
1225
|
-
# Spec file for spec-compliance role
|
|
1226
525
|
spec: SPEC.md
|
|
1227
526
|
|
|
1228
|
-
#
|
|
527
|
+
# Harness evidence delivery
|
|
528
|
+
harness:
|
|
529
|
+
telemetry: full # off | envelope | findings | full (default)
|
|
530
|
+
parseFailures: false # send a parse-failed call's raw answer (scrubbed, 32 KB cap)
|
|
531
|
+
|
|
532
|
+
# GitHub token (prefer the GITHUB_TOKEN environment variable)
|
|
1229
533
|
# githubToken: ghp_...
|
|
1230
534
|
```
|
|
1231
535
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
536
|
+
**Accepted but ignored.** These keys pass validation but have no effect, so
|
|
537
|
+
older config files keep loading: `reviewers` (use `--reviewer` for explicit
|
|
538
|
+
model:role pairs; the key is read only when reconstructing a legacy 4.1.x
|
|
539
|
+
retry roster), top-level `focus`, and `output.terminal`, `output.json`,
|
|
540
|
+
`output.jsonPath`, `output.markdown`, `output.markdownPath` and
|
|
541
|
+
`output.github`. rcl writes report files only when `--json-file` or
|
|
542
|
+
`--markdown` is given. `output.belowThresholdAppendix` is the only `output`
|
|
543
|
+
key in use.
|
|
544
|
+
|
|
545
|
+
**Custom roles.** A custom role whose name matches a built-in (case-insensitive)
|
|
546
|
+
overrides it and inherits the fields you omit; any other name creates a new
|
|
547
|
+
specialist role. `focus` lists the categories the role specializes in
|
|
548
|
+
(`security`, `correctness`, `best-practices`, `tests`, `api-design`); it feeds
|
|
549
|
+
the role-relevance part of the consensus score and defaults to
|
|
550
|
+
`[best-practices]` for new roles. `severityBias` maps a category to a factor:
|
|
551
|
+
above 1 tells the reviewer to pick the more severe of two adjacent severity
|
|
552
|
+
levels for that category, below 1 the less severe one, and 1 has no effect.
|
|
553
|
+
The bias becomes calibration guidance in the reviewer's prompt; consensus
|
|
554
|
+
scoring itself is bias-free. Built-in examples: `security-auditor` uses
|
|
555
|
+
`{ security: 1.2 }`, `bug-hunter` uses `{ correctness: 1.3 }`.
|
|
556
|
+
|
|
557
|
+
**Quorum.** `quorumFraction` closes a round once that share of blocking seats
|
|
558
|
+
has succeeded on every chunk; stragglers, including core models, can be
|
|
559
|
+
canceled. Secondary successes never count, and secondary calls still running at
|
|
560
|
+
closure are canceled. It also raises the report's blocking-health requirement.
|
|
561
|
+
The default is exactly 2/3 (leave it unset for that); the minimum is 2/3, and
|
|
562
|
+
1 disables early closure and waits for every call.
|
|
563
|
+
|
|
564
|
+
**Concurrency.** `concurrency` limits all blocking reviewer calls within one
|
|
565
|
+
rcl process. `providerConcurrency` adds stricter per-provider admission caps;
|
|
566
|
+
raising the global limit never bypasses them. The scheduler scans past a
|
|
567
|
+
saturated provider so calls for other providers continue without changing the
|
|
568
|
+
original result order. Anthropic defaults to two concurrent calls; providers
|
|
569
|
+
with no default or explicit entry use only the global limit. Separate rcl
|
|
570
|
+
processes do not share these limits; async reviewers and verification use their
|
|
571
|
+
own scheduling.
|
|
572
|
+
|
|
573
|
+
**Retries.** `maxRetries` limits additional adapter SDK invocations after the
|
|
574
|
+
first attempt; all attempts share the call's `timeout` and parent cancellation
|
|
575
|
+
signal. Supported transient connection failures and HTTP status errors may
|
|
576
|
+
retry; cancellation, expired deadlines, permanent TLS/configuration errors and
|
|
577
|
+
unusable output do not. Report reviews and `discuss` answers expose
|
|
578
|
+
`adapterAttempts` when observed; chunked reviews sum it only when every part
|
|
579
|
+
has a known count. It is neither a wire-request count nor a billing total:
|
|
580
|
+
lower-level activity and charges after ambiguous transport failures can be
|
|
581
|
+
unknown, and token usage is what the SDK response exposes, not proof of total
|
|
582
|
+
charges across retries.
|
|
583
|
+
|
|
584
|
+
**Reasoning effort.** The top-level `reasoningEffort` applies only to OpenRouter
|
|
585
|
+
reviewers (default `low`, a supported level for the default Kimi K3, which
|
|
586
|
+
advertises `low`, `high` and `max` — avoid `medium` for it). Check the
|
|
587
|
+
selected model's supported levels before overriding: unbounded reasoning makes
|
|
588
|
+
these models spend the whole completion budget thinking before they answer.
|
|
589
|
+
Direct Sol and Gemini reviewers use their provider defaults. Opus 5.5 reviews
|
|
590
|
+
explicitly use `high` effort, streaming, and a 65,536-token output ceiling so
|
|
591
|
+
thinking and findings share adequate headroom. Opus 5.5's API default is
|
|
592
|
+
`medium`; rcl sets `high` because a review gate is intelligence-sensitive work
|
|
1271
593
|
([Anthropic's effort guidance](https://platform.claude.com/docs/en/build-with-claude/effort)).
|
|
1272
|
-
The same profile applies when Fable 5.1 is configured explicitly.
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
`
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
runs update the spinner; redirected runs emit periodic heartbeat and bounded
|
|
1297
|
-
completion lines with status counters, so a long queue is distinguishable from
|
|
1298
|
-
a hung process.
|
|
594
|
+
The same profile applies when Fable 5.1 is configured explicitly. Effort labels
|
|
595
|
+
are provider-specific and do not imply equal compute or quality across models.
|
|
596
|
+
|
|
597
|
+
**Verifier.** The verifier uses three verdicts: `confirmed`, `refuted`, and
|
|
598
|
+
`insufficient_evidence`. Confirmation requires a reachable failure mechanism
|
|
599
|
+
and exact code excerpts from the supplied change; citations are checked against
|
|
600
|
+
that source before a claim can be promoted to `verified`. Missing context or
|
|
601
|
+
inability to refute a claim is not confirmation. This checks citation
|
|
602
|
+
provenance, not semantic truth: the verifier still has to reason correctly.
|
|
603
|
+
Infrastructure failures are recorded as `unavailable`, and an unavailable
|
|
604
|
+
verdict never promotes a finding. Historical `unrefuted` verdicts retain their
|
|
605
|
+
original meaning. The default
|
|
606
|
+
verifier `openai/gpt-6-astra` is used only when OpenAI is already in the
|
|
607
|
+
roster; otherwise rcl picks the first direct-API roster model, so the default
|
|
608
|
+
never sends the diff to a provider you configured away from. OpenRouter models
|
|
609
|
+
cannot verify. Astra defaults to `high` effort;
|
|
610
|
+
`gating.verificationReasoningEffort` accepts `low`, `medium`, `high`, `xhigh`
|
|
611
|
+
or `max` for an OpenAI verifier only (other verifiers keep their provider
|
|
612
|
+
defaults and reject the setting), and is recorded in the report header and
|
|
613
|
+
retained verification plan. The whole verification pass has a 10-minute
|
|
614
|
+
default deadline (`gating.verificationPassTimeout`) across all queued verifier
|
|
615
|
+
batches; verifier calls default to the remaining pass budget, and the optional
|
|
616
|
+
`gating.verificationTimeout` (milliseconds) imposes a shorter per-call limit,
|
|
617
|
+
still capped by the remaining pass deadline.
|
|
1299
618
|
|
|
1300
619
|
---
|
|
1301
620
|
|
|
1302
|
-
##
|
|
621
|
+
## Environment variables
|
|
1303
622
|
|
|
1304
|
-
|
|
623
|
+
| Variable | Description |
|
|
624
|
+
| --- | --- |
|
|
625
|
+
| `ANTHROPIC_API_KEY` | Anthropic models |
|
|
626
|
+
| `OPENAI_API_KEY` | OpenAI models and the default verifier |
|
|
627
|
+
| `GOOGLE_API_KEY` | Google Gemini; empty or whitespace-only values fall through |
|
|
628
|
+
| `GEMINI_API_KEY` | Google Gemini when `GOOGLE_API_KEY` is absent or blank; also used for Harness-injected keys |
|
|
629
|
+
| `OPENROUTER_API_KEY` | `openrouter/…` models |
|
|
630
|
+
| `OPENAI_COMPAT_BASE_URL` | Base URL for `openai-compat` models and unrecognized unprefixed names (default `http://localhost:11434/v1`) |
|
|
631
|
+
| `OPENAI_COMPAT_API_KEY` | API key for that endpoint (default: the placeholder `local`) |
|
|
632
|
+
| `GITHUB_TOKEN` | GitHub token for PR fetch and `--post` |
|
|
633
|
+
| `XDG_CONFIG_HOME` | Where the `harness login` credential is read from (`$XDG_CONFIG_HOME/harness/credentials.json`; default `~/.config/harness/credentials.json`) |
|
|
634
|
+
| `RCL_DATA_DIR` | Per-machine state directory (model stats, evidence outbox and quarantine); default `~/.rcl` |
|
|
635
|
+
| `RCL_TELEMETRY` | `off` keeps every review on the machine |
|
|
636
|
+
| `RCL_NO_HARNESS_KEYS` | Any value disables provider-key distribution via Harness |
|
|
637
|
+
| `RCL_FOR_PR` | Same as `--for-pr`, for patch-file reviews |
|
|
638
|
+
| `RCL_CONVERGE_TARGET` / `RCL_CONVERGE_ROUND` / `RCL_CONVERGE_ATTEMPT` | Same as `--converge-target` / `--round` / `--attempt` |
|
|
639
|
+
| `RCL_DEBUG` | Any value prints full error stack traces |
|
|
640
|
+
| `HARNESS_API_TOKEN` | CI credential for evidence delivery; requires `HARNESS_API_URL` and never pairs with the stored login host |
|
|
641
|
+
| `HARNESS_API_URL` | The Harness host `HARNESS_API_TOKEN` was minted by; under `--attest`, the host attested to (no token needed) |
|
|
642
|
+
| `ACTIONS_ID_TOKEN_REQUEST_URL` / `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | Set by the GitHub Actions runner for jobs with `id-token: write`; `--attest` requires them |
|
|
1305
643
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
4. **Filtered** — groups below `minConsensusScore` or `minConfidence` are demoted (blocking severities are never dropped). Demoted findings land in a collapsed "worth checking" appendix at the bottom of the report and in the JSON `belowThresholdFindings` field — never in severity totals or CI gating. Set `output.belowThresholdAppendix: false` to drop them outright instead.
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
## How consensus and gating work
|
|
1310
647
|
|
|
1311
|
-
|
|
648
|
+
When multiple models and roles review the same diff, their findings are:
|
|
649
|
+
|
|
650
|
+
1. **Deduplicated** — findings on the same file and overlapping line range are
|
|
651
|
+
grouped by weighted title+description token similarity; findings in
|
|
652
|
+
different categories can still merge, but need stronger similarity. Findings
|
|
653
|
+
whose line ranges strictly overlap and that name the same issue concept (SQL
|
|
654
|
+
injection, IDOR, hardcoded secret, …) merge regardless of wording. Repeats
|
|
655
|
+
within a single review are collapsed first. Findings that clearly reach
|
|
656
|
+
opposite conclusions are kept as separate, disputed findings; subtler
|
|
657
|
+
contradictions merge but are flagged as disputed.
|
|
658
|
+
2. **Scored** — each group receives a consensus score from three dimensions:
|
|
659
|
+
reviewer diversity (how many distinct models and roles flagged it, saturating
|
|
660
|
+
at half the fleet), role relevance (whether a role specialized in that
|
|
661
|
+
finding type confirmed it), and isolation (what fraction of relevant
|
|
662
|
+
reviewers flagged it).
|
|
663
|
+
3. **Classified** — groups get a confidence band (Very High → Minimal) and a
|
|
664
|
+
final severity. Severity is the most common rating across reviewers; when
|
|
665
|
+
reviewers disagree, high-confidence agreement elevates it, but only to a
|
|
666
|
+
severity at least two reviewers independently assigned — a lone outlier
|
|
667
|
+
rating is surfaced as a dispute instead. Each group also gets an
|
|
668
|
+
**agreement tier** measured over distinct models — `unanimous` (every
|
|
669
|
+
successful model), `majority` (at least half), `minority` (2+, under half),
|
|
670
|
+
`single` (one model) — because roles share a model's blind spots.
|
|
671
|
+
4. **Filtered** — groups below `minConsensusScore` or `minConfidence` are
|
|
672
|
+
demoted (blocking severities are never dropped). Demoted findings land in a
|
|
673
|
+
collapsed "worth checking" appendix at the bottom of the report and in the
|
|
674
|
+
JSON `belowThresholdFindings` field — never in severity totals or CI gating.
|
|
675
|
+
Set `output.belowThresholdAppendix: false` to drop them outright.
|
|
676
|
+
|
|
677
|
+
The report is organized by agreement tier — unanimous first, then majority,
|
|
678
|
+
minority, **disputed** (rendered as per-model positions so you can judge), and
|
|
679
|
+
single-model last. Within each tier, findings sort by severity. The tiers tell
|
|
680
|
+
you which findings are independently confirmed and where to spend your own
|
|
681
|
+
judgment.
|
|
682
|
+
|
|
683
|
+
**Gating** decides which findings block convergence and CI. In the default
|
|
684
|
+
`verified-consensus` mode only critical and important findings can gate, and
|
|
685
|
+
each gets a `gating.reason`:
|
|
686
|
+
|
|
687
|
+
| `gating.reason` | When |
|
|
688
|
+
| --- | --- |
|
|
689
|
+
| `consensus` | Raised by at least `gating.minModels` (default 2) distinct models; model weights can demote consensus but never replace distinct models |
|
|
690
|
+
| `critical` | Critical severity |
|
|
691
|
+
| `verified` | A single-model important finding the verifier confirmed with source evidence |
|
|
692
|
+
| `none` | Everything else, including refuted, insufficient-evidence and unverified (`unavailable`) claims; still reported, never blocking |
|
|
1312
693
|
|
|
1313
|
-
|
|
694
|
+
`gating.mode: all-findings` restores the legacy rule where every critical or
|
|
695
|
+
important finding gates. For the full scoring algorithm, see
|
|
696
|
+
[CONSENSUS_V2_SPEC.md](https://github.com/allocator-one/rcl/blob/main/CONSENSUS_V2_SPEC.md).
|
|
1314
697
|
|
|
1315
698
|
---
|
|
1316
699
|
|
|
1317
|
-
##
|
|
700
|
+
## Harness evidence and gate
|
|
701
|
+
|
|
702
|
+
In a repository that carries `.harness-cli/config.json`, with a
|
|
703
|
+
`harness login` (or `HARNESS_API_TOKEN` + `HARNESS_API_URL` in CI), every
|
|
704
|
+
review is recorded on [Harness](https://harness.infra.one) after the report is
|
|
705
|
+
written: the run header, findings, reviewer calls, stats and — at the default
|
|
706
|
+
`full` level — both report files, scrubbed for key-shaped strings. Provider
|
|
707
|
+
keys, tokens, environment variables and prompts are never sent, and raw model
|
|
708
|
+
answers only when `harness.parseFailures: true` opts in for parse-failed calls.
|
|
709
|
+
The review never blocks on the
|
|
710
|
+
network: an outage spools the evidence to `~/.rcl/outbox/` for
|
|
711
|
+
`rcl telemetry flush`. One status line reports the outcome, and
|
|
712
|
+
`--evidence-required` turns a missing acknowledgment into exit 4. Opt out with
|
|
713
|
+
`--no-telemetry`, `RCL_TELEMETRY=off` or `harness.telemetry: off`.
|
|
714
|
+
|
|
715
|
+
Harness computes two projections per pull request head from that evidence:
|
|
716
|
+
|
|
717
|
+
- **advisory** — counts rounds recorded with any credential, such as a
|
|
718
|
+
developer's `harness login` or a CI token;
|
|
719
|
+
- **enforced** — counts only **attested** rounds, recorded by the
|
|
720
|
+
organization's gate workflow; attested rounds also drive the
|
|
721
|
+
`Review Council` check run.
|
|
722
|
+
|
|
723
|
+
A round counts only when it names the pull request, comes from the same
|
|
724
|
+
repository and reviewed the pull request's current head.
|
|
725
|
+
|
|
726
|
+
`rcl evidence status owner/repo#N` prints both and exits 0 only when the judged
|
|
727
|
+
projection (advisory, or enforced with `--enforced`) is `converged`.
|
|
728
|
+
|
|
729
|
+
- [Telemetry and evidence](https://github.com/allocator-one/rcl/blob/main/docs/telemetry-and-evidence.md)
|
|
730
|
+
— delivery, attestation, evidence reads and the evidence recovery commands
|
|
731
|
+
- [Convergence and recovery](https://github.com/allocator-one/rcl/blob/main/docs/convergence.md)
|
|
732
|
+
— the convergence loop, budgets, reviewer health and recovery operations
|
|
733
|
+
- [Review evidence recovery](https://github.com/allocator-one/rcl/blob/main/docs/review-evidence-recovery.md)
|
|
734
|
+
— operator runbook for the gate's encrypted evidence artifact
|
|
735
|
+
|
|
736
|
+
### The gate workflow
|
|
737
|
+
|
|
738
|
+
Once a pull request head's advisory status has converged, Harness dispatches
|
|
739
|
+
the organization's gate workflow for that head. Inside the job,
|
|
740
|
+
`rcl review owner/repo#N --attest` exchanges the job's OIDC token for a
|
|
741
|
+
run-bound Harness credential, so the round it records is attested. Harness
|
|
742
|
+
accepts it only if the workflow file is on the organization's gate allow-list
|
|
743
|
+
at its default branch, the reviewed head is the pull request's current head,
|
|
744
|
+
and the pull request is not from a fork. `--attest` never falls back to another
|
|
745
|
+
credential and implies `--evidence-required`.
|
|
746
|
+
|
|
747
|
+
The workflow contract, as in this repository's
|
|
748
|
+
[`review_gate.yml`](https://github.com/allocator-one/rcl/blob/main/.github/workflows/review_gate.yml):
|
|
749
|
+
|
|
750
|
+
- `workflow_dispatch` inputs `pr_number` and `head_sha` (required) and
|
|
751
|
+
`attempt_id` (optional: the Harness dispatch attempt UUID, omitted only for
|
|
752
|
+
an unregistered manual run);
|
|
753
|
+
- a `# harness-review-lifecycle: 1` comment, which promises the optional
|
|
754
|
+
`attempt_id` input and the exact run-name
|
|
755
|
+
`Review Council gate · ${{ inputs.attempt_id || 'unregistered' }}`;
|
|
756
|
+
- `id-token: write` for the review job, no provider secrets, and the pull
|
|
757
|
+
request never checked out;
|
|
758
|
+
- the latest release installed by the verified installer, which checks the
|
|
759
|
+
package against the registry's SHA-512 integrity before installing.
|
|
760
|
+
|
|
761
|
+
Abridged:
|
|
1318
762
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
explains how to check private-repository access without exposing credentials.
|
|
1324
|
-
Local patch reviews do not read GitHub credentials.
|
|
763
|
+
```yaml
|
|
764
|
+
# harness-review-lifecycle: 1
|
|
765
|
+
name: Review Council gate
|
|
766
|
+
run-name: Review Council gate · ${{ inputs.attempt_id || 'unregistered' }}
|
|
1325
767
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
| `OPENROUTER_API_KEY` | API key for [OpenRouter](https://openrouter.ai) models (`openrouter/…` prefix) |
|
|
1333
|
-
| `GITHUB_TOKEN` | GitHub personal access token (PR fetch and post) |
|
|
1334
|
-
| `RCL_DEBUG` | Set to any value to print full error stack traces |
|
|
1335
|
-
| `RCL_NO_HARNESS_KEYS` | Set to any value to disable Harness key distribution (below) |
|
|
1336
|
-
| `RCL_TELEMETRY` | `off` keeps every review on the machine (see `rcl telemetry`) |
|
|
1337
|
-
| `HARNESS_API_TOKEN` | CI credential for evidence delivery; requires `HARNESS_API_URL` — never pairs with the stored login host |
|
|
1338
|
-
| `HARNESS_API_URL` | The Harness host `HARNESS_API_TOKEN` was minted by; under `--attest` the host attested to (no token needed) |
|
|
1339
|
-
| `ACTIONS_ID_TOKEN_REQUEST_URL` / `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | Set by the GitHub Actions runner for jobs with `id-token: write`; `--attest` reads them and refuses to run without them |
|
|
1340
|
-
|
|
1341
|
-
The default blocking council is direct-API only (Anthropic, OpenAI, Google) —
|
|
1342
|
-
no default review round ever waits on an OpenRouter-routed call. The default
|
|
1343
|
-
async lane holds one OpenRouter-hosted bonus reviewer (`kimi-k3`); if
|
|
1344
|
-
`OPENROUTER_API_KEY` is not set, it is dropped from the defaults with a warning
|
|
1345
|
-
(models you configure explicitly still fail loudly instead). Note that when the
|
|
1346
|
-
key is set, default reviews send diff and context content to OpenRouter — an
|
|
1347
|
-
aggregator and an additional data processor beyond the direct model providers —
|
|
1348
|
-
as well as to Anthropic, OpenAI, and Google. Configure `models:` and
|
|
1349
|
-
`asyncModels:` explicitly if that matters for your repository.
|
|
1350
|
-
|
|
1351
|
-
### Key distribution via Harness
|
|
1352
|
-
|
|
1353
|
-
Repos that carry a committed `.harness-cli/config.json` (discovered git-style,
|
|
1354
|
-
walking up from the working directory) can get their provider keys from a
|
|
1355
|
-
[Harness](https://harness.infra.one) backend instead of every teammate managing
|
|
1356
|
-
them by hand: run `harness login` once, and any provider key **missing from the
|
|
1357
|
-
environment** is fetched from `GET /api/v1/model-keys` on the host that minted
|
|
1358
|
-
the stored login token, and injected for the run.
|
|
1359
|
-
|
|
1360
|
-
- Environment variables always win — only missing keys are injected.
|
|
1361
|
-
- The stored credential is only ever sent to the host it was minted for, never
|
|
1362
|
-
to a URL named by the repo's own config (untrusted input in a cloned repo).
|
|
1363
|
-
- Any failure — not logged in, offline, older backend without the endpoint —
|
|
1364
|
-
falls back silently to the plain-environment behavior above. The fetch runs
|
|
1365
|
-
under a 3-second timeout and keys are never written to disk or logs.
|
|
1366
|
-
- Which providers the backend serves is server configuration
|
|
1367
|
-
(`HARNESS_MODEL_KEYS` on the backend); `RCL_NO_HARNESS_KEYS` disables the
|
|
1368
|
-
whole mechanism client-side.
|
|
768
|
+
on:
|
|
769
|
+
workflow_dispatch:
|
|
770
|
+
inputs:
|
|
771
|
+
pr_number: { required: true, type: string }
|
|
772
|
+
head_sha: { required: true, type: string }
|
|
773
|
+
attempt_id: { required: false, type: string }
|
|
1369
774
|
|
|
1370
|
-
|
|
775
|
+
permissions:
|
|
776
|
+
contents: read
|
|
1371
777
|
|
|
1372
|
-
|
|
778
|
+
jobs:
|
|
779
|
+
attested-review:
|
|
780
|
+
runs-on: ubuntu-24.04
|
|
781
|
+
permissions:
|
|
782
|
+
id-token: write
|
|
783
|
+
contents: read
|
|
784
|
+
pull-requests: read
|
|
785
|
+
env:
|
|
786
|
+
HARNESS_API_URL: https://harness.infra.one
|
|
787
|
+
steps:
|
|
788
|
+
# Check out this workflow's own commit (never the pull request), set up
|
|
789
|
+
# Node, and install the latest release with the verified installer.
|
|
790
|
+
# See the full workflow for these steps and for the encrypted
|
|
791
|
+
# retention of the review evidence.
|
|
792
|
+
- name: Attested review
|
|
793
|
+
env:
|
|
794
|
+
PR_NUMBER: ${{ inputs.pr_number }}
|
|
795
|
+
HEAD_SHA: ${{ inputs.head_sha }}
|
|
796
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
797
|
+
run: |
|
|
798
|
+
# The full workflow validates PR_NUMBER, HEAD_SHA and attempt_id first.
|
|
799
|
+
rcl review "$GITHUB_REPOSITORY#$PR_NUMBER" \
|
|
800
|
+
--attest \
|
|
801
|
+
--expect-head-sha "$HEAD_SHA" \
|
|
802
|
+
--evidence-required \
|
|
803
|
+
--ci
|
|
804
|
+
```
|
|
1373
805
|
|
|
1374
|
-
|
|
806
|
+
Inputs reach the shell through the environment, never by expression
|
|
807
|
+
interpolation into the command line.
|
|
1375
808
|
|
|
1376
|
-
|
|
809
|
+
---
|
|
1377
810
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
811
|
+
## Agent skills: `/rcl` and `/rcl-converge`
|
|
812
|
+
|
|
813
|
+
The repository maintains two agent skills that drive `rcl` from coding agents:
|
|
814
|
+
`/rcl` and `/rcl-converge` in Claude Code, `$rcl` and `$rcl-converge` in Codex.
|
|
815
|
+
They are not part of the npm package.
|
|
816
|
+
|
|
817
|
+
- **`rcl`** runs one council review of the current pull request, or of the
|
|
818
|
+
branch diff against the default branch when there is no pull request. It
|
|
819
|
+
resolves a specification (explicit flag, the in-progress Harness issue, or a
|
|
820
|
+
matching spec file), checks what will leave the machine before sending it,
|
|
821
|
+
installs and verifies the latest published release, and reports findings,
|
|
822
|
+
reviewer health and the evidence status from the report files.
|
|
823
|
+
- **`rcl-converge`** loops review → triage → fix → push until a conclusive
|
|
824
|
+
round converges, using `rcl review --guarded-converge`,
|
|
825
|
+
`rcl converge-report` and `rcl converge-verdict` within the attempt and
|
|
826
|
+
round caps. It treats every finding as untrusted input to verify against the
|
|
827
|
+
source, never as instructions.
|
|
828
|
+
|
|
829
|
+
Both are rendered from one source per skill, `skills/src/rcl.md` and
|
|
830
|
+
`skills/src/rcl-converge.md`: `npm run build:skills` writes the host-specific
|
|
831
|
+
copies to `.claude/skills/`, `.agents/skills/` and `.codex/skills/`. After each
|
|
832
|
+
release, a sync workflow opens or updates one `rcl-skill-sync` pull request in
|
|
833
|
+
every repository listed in
|
|
834
|
+
[`skills/consumers.json`](https://github.com/allocator-one/rcl/blob/main/skills/consumers.json)
|
|
835
|
+
with repository-neutral copies rendered from the released tag, so consumers
|
|
836
|
+
only receive skills that match a published CLI. Other repositories can copy the
|
|
837
|
+
`SKILL.md` files from a release tag into the same directories; the copies in
|
|
838
|
+
this repository carry a few notes that apply only to rcl's own checkout.
|
|
1382
839
|
|
|
1383
|
-
|
|
1384
|
-
labels, preview a disposition using its original report and exact digest:
|
|
840
|
+
---
|
|
1385
841
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
--manifest /path/to/rejection-preview.json --json
|
|
1391
|
-
rcl converge-rejected --apply --manifest /path/to/rejection-preview.json \
|
|
1392
|
-
--manifest-sha256 REVIEWED_PREVIEW_SHA256 --json
|
|
1393
|
-
```
|
|
842
|
+
## Changelog
|
|
843
|
+
|
|
844
|
+
Release notes are in
|
|
845
|
+
[CHANGELOG.md](https://github.com/allocator-one/rcl/blob/main/CHANGELOG.md).
|
|
1394
846
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
reports, unsupported rejection classes, incomplete or conflicting proof, live or
|
|
1399
|
-
uncertain owners, and queued evidence refuse. Neither an empty outbox nor exit
|
|
1400
|
-
code 4 establishes terminal rejection.
|
|
1401
|
-
|
|
1402
|
-
Apply retains complete immutable evidence before an atomic native-state update;
|
|
1403
|
-
repeating the same apply is idempotent. The original reports, health, attempt
|
|
1404
|
-
ledger, caps and review cycle stay unchanged. The native audit permanently bars
|
|
1405
|
-
admission of the rejected original. A later normal guarded review still requires
|
|
1406
|
-
an explicit bounded `--retry-reason`, normal preflight and remaining budget; it
|
|
1407
|
-
claims the next attempt in the same cycle and native round. Recovery itself
|
|
1408
|
-
uploads nothing, starts no provider calls and provides no convergence or approval.
|
|
1409
|
-
|
|
1410
|
-
Queue checks are fail-closed observations, not a new global outbox lock. This
|
|
1411
|
-
narrow recovery proves rejection before network delivery and requires the
|
|
1412
|
-
original producer to have exited. It rechecks for queued evidence at apply and
|
|
1413
|
-
before the later guarded claim. It never treats a server/transport refusal as
|
|
1414
|
-
that proof. Historical audits use their retained copies, so ordinary temporary
|
|
1415
|
-
source cleanup does not break subsequent review and admission.
|
|
847
|
+
## License
|
|
848
|
+
|
|
849
|
+
MIT © 2026 Michael Ströck
|