@allocator-one/rcl 4.4.19

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.
Files changed (663) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1415 -0
  3. package/dist/ci.d.ts +16 -0
  4. package/dist/ci.js +42 -0
  5. package/dist/ci.js.map +1 -0
  6. package/dist/config/data-dir.d.ts +6 -0
  7. package/dist/config/data-dir.js +12 -0
  8. package/dist/config/data-dir.js.map +1 -0
  9. package/dist/config/defaults.d.ts +82 -0
  10. package/dist/config/defaults.js +89 -0
  11. package/dist/config/defaults.js.map +1 -0
  12. package/dist/config/harness.d.ts +40 -0
  13. package/dist/config/harness.js +151 -0
  14. package/dist/config/harness.js.map +1 -0
  15. package/dist/config/loader.d.ts +13 -0
  16. package/dist/config/loader.js +131 -0
  17. package/dist/config/loader.js.map +1 -0
  18. package/dist/config/provider-concurrency.d.ts +4 -0
  19. package/dist/config/provider-concurrency.js +6 -0
  20. package/dist/config/provider-concurrency.js.map +1 -0
  21. package/dist/config/providers.d.ts +3 -0
  22. package/dist/config/providers.js +9 -0
  23. package/dist/config/providers.js.map +1 -0
  24. package/dist/config/schema.d.ts +179 -0
  25. package/dist/config/schema.js +115 -0
  26. package/dist/config/schema.js.map +1 -0
  27. package/dist/consensus/claim-contract.d.ts +9 -0
  28. package/dist/consensus/claim-contract.js +307 -0
  29. package/dist/consensus/claim-contract.js.map +1 -0
  30. package/dist/consensus/claim-identity.d.ts +5 -0
  31. package/dist/consensus/claim-identity.js +39 -0
  32. package/dist/consensus/claim-identity.js.map +1 -0
  33. package/dist/consensus/deduper.d.ts +43 -0
  34. package/dist/consensus/deduper.js +1052 -0
  35. package/dist/consensus/deduper.js.map +1 -0
  36. package/dist/consensus/finding-identity.d.ts +20 -0
  37. package/dist/consensus/finding-identity.js +49 -0
  38. package/dist/consensus/finding-identity.js.map +1 -0
  39. package/dist/consensus/gating.d.ts +215 -0
  40. package/dist/consensus/gating.js +699 -0
  41. package/dist/consensus/gating.js.map +1 -0
  42. package/dist/consensus/parser.d.ts +22 -0
  43. package/dist/consensus/parser.js +255 -0
  44. package/dist/consensus/parser.js.map +1 -0
  45. package/dist/consensus/semantic-deduper.d.ts +10 -0
  46. package/dist/consensus/semantic-deduper.js +70 -0
  47. package/dist/consensus/semantic-deduper.js.map +1 -0
  48. package/dist/consensus/types.d.ts +196 -0
  49. package/dist/consensus/types.js +2 -0
  50. package/dist/consensus/types.js.map +1 -0
  51. package/dist/consensus/verification-contract.d.ts +8 -0
  52. package/dist/consensus/verification-contract.js +95 -0
  53. package/dist/consensus/verification-contract.js.map +1 -0
  54. package/dist/consensus/voter.d.ts +29 -0
  55. package/dist/consensus/voter.js +394 -0
  56. package/dist/consensus/voter.js.map +1 -0
  57. package/dist/converge/attempt-budget.d.ts +88 -0
  58. package/dist/converge/attempt-budget.js +717 -0
  59. package/dist/converge/attempt-budget.js.map +1 -0
  60. package/dist/converge/bound-fix-recovery-source.d.ts +24 -0
  61. package/dist/converge/bound-fix-recovery-source.js +25 -0
  62. package/dist/converge/bound-fix-recovery-source.js.map +1 -0
  63. package/dist/converge/bound-fix-recovery.d.ts +15 -0
  64. package/dist/converge/bound-fix-recovery.js +65 -0
  65. package/dist/converge/bound-fix-recovery.js.map +1 -0
  66. package/dist/converge/correction-anchors.d.ts +1 -0
  67. package/dist/converge/correction-anchors.js +2 -0
  68. package/dist/converge/correction-anchors.js.map +1 -0
  69. package/dist/converge/cycle-remote.d.ts +6 -0
  70. package/dist/converge/cycle-remote.js +41 -0
  71. package/dist/converge/cycle-remote.js.map +1 -0
  72. package/dist/converge/delivery-reconciliation.d.ts +17 -0
  73. package/dist/converge/delivery-reconciliation.js +53 -0
  74. package/dist/converge/delivery-reconciliation.js.map +1 -0
  75. package/dist/converge/finding-identity.d.ts +6 -0
  76. package/dist/converge/finding-identity.js +7 -0
  77. package/dist/converge/finding-identity.js.map +1 -0
  78. package/dist/converge/fresh-review.d.ts +37 -0
  79. package/dist/converge/fresh-review.js +351 -0
  80. package/dist/converge/fresh-review.js.map +1 -0
  81. package/dist/converge/launch-guard.d.ts +55 -0
  82. package/dist/converge/launch-guard.js +363 -0
  83. package/dist/converge/launch-guard.js.map +1 -0
  84. package/dist/converge/launch-preflight.d.ts +5 -0
  85. package/dist/converge/launch-preflight.js +41 -0
  86. package/dist/converge/launch-preflight.js.map +1 -0
  87. package/dist/converge/launch-record.d.ts +136 -0
  88. package/dist/converge/launch-record.js +86 -0
  89. package/dist/converge/launch-record.js.map +1 -0
  90. package/dist/converge/legacy-launch-health.d.ts +311 -0
  91. package/dist/converge/legacy-launch-health.js +159 -0
  92. package/dist/converge/legacy-launch-health.js.map +1 -0
  93. package/dist/converge/legacy-roster.d.ts +29 -0
  94. package/dist/converge/legacy-roster.js +268 -0
  95. package/dist/converge/legacy-roster.js.map +1 -0
  96. package/dist/converge/native-lock.d.ts +18 -0
  97. package/dist/converge/native-lock.js +122 -0
  98. package/dist/converge/native-lock.js.map +1 -0
  99. package/dist/converge/ordinary-pending-export.d.ts +67 -0
  100. package/dist/converge/ordinary-pending-export.js +175 -0
  101. package/dist/converge/ordinary-pending-export.js.map +1 -0
  102. package/dist/converge/ordinary-pending-package.d.ts +28 -0
  103. package/dist/converge/ordinary-pending-package.js +36 -0
  104. package/dist/converge/ordinary-pending-package.js.map +1 -0
  105. package/dist/converge/pending-legacy-resume.d.ts +119 -0
  106. package/dist/converge/pending-legacy-resume.js +720 -0
  107. package/dist/converge/pending-legacy-resume.js.map +1 -0
  108. package/dist/converge/pending-recovery-source.d.ts +131 -0
  109. package/dist/converge/pending-recovery-source.js +71 -0
  110. package/dist/converge/pending-recovery-source.js.map +1 -0
  111. package/dist/converge/recovery-launch.d.ts +66 -0
  112. package/dist/converge/recovery-launch.js +518 -0
  113. package/dist/converge/recovery-launch.js.map +1 -0
  114. package/dist/converge/retained-report.d.ts +29 -0
  115. package/dist/converge/retained-report.js +218 -0
  116. package/dist/converge/retained-report.js.map +1 -0
  117. package/dist/converge/retry-source.d.ts +25 -0
  118. package/dist/converge/retry-source.js +13 -0
  119. package/dist/converge/retry-source.js.map +1 -0
  120. package/dist/converge/review-cycle.d.ts +49 -0
  121. package/dist/converge/review-cycle.js +26 -0
  122. package/dist/converge/review-cycle.js.map +1 -0
  123. package/dist/converge/round-gap-schema.d.ts +66 -0
  124. package/dist/converge/round-gap-schema.js +51 -0
  125. package/dist/converge/round-gap-schema.js.map +1 -0
  126. package/dist/converge/round-gap.d.ts +38 -0
  127. package/dist/converge/round-gap.js +334 -0
  128. package/dist/converge/round-gap.js.map +1 -0
  129. package/dist/converge/run-state.d.ts +224 -0
  130. package/dist/converge/run-state.js +564 -0
  131. package/dist/converge/run-state.js.map +1 -0
  132. package/dist/converge/stale-report-schema.d.ts +42 -0
  133. package/dist/converge/stale-report-schema.js +79 -0
  134. package/dist/converge/stale-report-schema.js.map +1 -0
  135. package/dist/converge/stale-report-storage.d.ts +47 -0
  136. package/dist/converge/stale-report-storage.js +172 -0
  137. package/dist/converge/stale-report-storage.js.map +1 -0
  138. package/dist/converge/stale-report.d.ts +14 -0
  139. package/dist/converge/stale-report.js +213 -0
  140. package/dist/converge/stale-report.js.map +1 -0
  141. package/dist/converge/target-ownership.d.ts +29 -0
  142. package/dist/converge/target-ownership.js +95 -0
  143. package/dist/converge/target-ownership.js.map +1 -0
  144. package/dist/converge/terminal-rejection-schema.d.ts +42 -0
  145. package/dist/converge/terminal-rejection-schema.js +39 -0
  146. package/dist/converge/terminal-rejection-schema.js.map +1 -0
  147. package/dist/converge/terminal-rejection.d.ts +13 -0
  148. package/dist/converge/terminal-rejection.js +275 -0
  149. package/dist/converge/terminal-rejection.js.map +1 -0
  150. package/dist/coordination/registry-lock.d.ts +82 -0
  151. package/dist/coordination/registry-lock.js +498 -0
  152. package/dist/coordination/registry-lock.js.map +1 -0
  153. package/dist/discuss.d.ts +53 -0
  154. package/dist/discuss.js +112 -0
  155. package/dist/discuss.js.map +1 -0
  156. package/dist/dispatch/adapter.d.ts +32 -0
  157. package/dist/dispatch/adapter.js +2 -0
  158. package/dist/dispatch/adapter.js.map +1 -0
  159. package/dist/dispatch/anthropic.d.ts +10 -0
  160. package/dist/dispatch/anthropic.js +235 -0
  161. package/dist/dispatch/anthropic.js.map +1 -0
  162. package/dist/dispatch/async-lane.d.ts +127 -0
  163. package/dist/dispatch/async-lane.js +414 -0
  164. package/dist/dispatch/async-lane.js.map +1 -0
  165. package/dist/dispatch/capture-council.d.ts +63 -0
  166. package/dist/dispatch/capture-council.js +137 -0
  167. package/dist/dispatch/capture-council.js.map +1 -0
  168. package/dist/dispatch/captured-async.d.ts +67 -0
  169. package/dist/dispatch/captured-async.js +40 -0
  170. package/dist/dispatch/captured-async.js.map +1 -0
  171. package/dist/dispatch/captured-inputs.d.ts +50 -0
  172. package/dist/dispatch/captured-inputs.js +190 -0
  173. package/dist/dispatch/captured-inputs.js.map +1 -0
  174. package/dist/dispatch/checkpoint-async-context.d.ts +27 -0
  175. package/dist/dispatch/checkpoint-async-context.js +51 -0
  176. package/dist/dispatch/checkpoint-async-context.js.map +1 -0
  177. package/dist/dispatch/checkpoint-async-execution.d.ts +22 -0
  178. package/dist/dispatch/checkpoint-async-execution.js +130 -0
  179. package/dist/dispatch/checkpoint-async-execution.js.map +1 -0
  180. package/dist/dispatch/checkpoint-async-store.d.ts +134 -0
  181. package/dist/dispatch/checkpoint-async-store.js +534 -0
  182. package/dist/dispatch/checkpoint-async-store.js.map +1 -0
  183. package/dist/dispatch/checkpoint-async-unknown.d.ts +19 -0
  184. package/dist/dispatch/checkpoint-async-unknown.js +46 -0
  185. package/dist/dispatch/checkpoint-async-unknown.js.map +1 -0
  186. package/dist/dispatch/checkpoint-async.d.ts +252 -0
  187. package/dist/dispatch/checkpoint-async.js +270 -0
  188. package/dist/dispatch/checkpoint-async.js.map +1 -0
  189. package/dist/dispatch/checkpoint-gating-execution.d.ts +26 -0
  190. package/dist/dispatch/checkpoint-gating-execution.js +84 -0
  191. package/dist/dispatch/checkpoint-gating-execution.js.map +1 -0
  192. package/dist/dispatch/checkpoint-phase-limits.d.ts +5 -0
  193. package/dist/dispatch/checkpoint-phase-limits.js +11 -0
  194. package/dist/dispatch/checkpoint-phase-limits.js.map +1 -0
  195. package/dist/dispatch/checkpoint-verification-context.d.ts +15 -0
  196. package/dist/dispatch/checkpoint-verification-context.js +48 -0
  197. package/dist/dispatch/checkpoint-verification-context.js.map +1 -0
  198. package/dist/dispatch/checkpoint-verification.d.ts +213 -0
  199. package/dist/dispatch/checkpoint-verification.js +381 -0
  200. package/dist/dispatch/checkpoint-verification.js.map +1 -0
  201. package/dist/dispatch/checkpoint.d.ts +329 -0
  202. package/dist/dispatch/checkpoint.js +1418 -0
  203. package/dist/dispatch/checkpoint.js.map +1 -0
  204. package/dist/dispatch/google.d.ts +10 -0
  205. package/dist/dispatch/google.js +158 -0
  206. package/dist/dispatch/google.js.map +1 -0
  207. package/dist/dispatch/late-audit.d.ts +23 -0
  208. package/dist/dispatch/late-audit.js +133 -0
  209. package/dist/dispatch/late-audit.js.map +1 -0
  210. package/dist/dispatch/merge.d.ts +29 -0
  211. package/dist/dispatch/merge.js +112 -0
  212. package/dist/dispatch/merge.js.map +1 -0
  213. package/dist/dispatch/openai-compat.d.ts +30 -0
  214. package/dist/dispatch/openai-compat.js +166 -0
  215. package/dist/dispatch/openai-compat.js.map +1 -0
  216. package/dist/dispatch/openai.d.ts +10 -0
  217. package/dist/dispatch/openai.js +159 -0
  218. package/dist/dispatch/openai.js.map +1 -0
  219. package/dist/dispatch/original-execution.d.ts +30 -0
  220. package/dist/dispatch/original-execution.js +91 -0
  221. package/dist/dispatch/original-execution.js.map +1 -0
  222. package/dist/dispatch/original-launch.d.ts +37 -0
  223. package/dist/dispatch/original-launch.js +117 -0
  224. package/dist/dispatch/original-launch.js.map +1 -0
  225. package/dist/dispatch/quorum.d.ts +15 -0
  226. package/dist/dispatch/quorum.js +33 -0
  227. package/dist/dispatch/quorum.js.map +1 -0
  228. package/dist/dispatch/recovery-operation.d.ts +44 -0
  229. package/dist/dispatch/recovery-operation.js +146 -0
  230. package/dist/dispatch/recovery-operation.js.map +1 -0
  231. package/dist/dispatch/recovery-policy.d.ts +54 -0
  232. package/dist/dispatch/recovery-policy.js +157 -0
  233. package/dist/dispatch/recovery-policy.js.map +1 -0
  234. package/dist/dispatch/recovery.d.ts +78 -0
  235. package/dist/dispatch/recovery.js +299 -0
  236. package/dist/dispatch/recovery.js.map +1 -0
  237. package/dist/dispatch/retained-async.d.ts +13 -0
  238. package/dist/dispatch/retained-async.js +101 -0
  239. package/dist/dispatch/retained-async.js.map +1 -0
  240. package/dist/dispatch/reviewer-identity.d.ts +10 -0
  241. package/dist/dispatch/reviewer-identity.js +19 -0
  242. package/dist/dispatch/reviewer-identity.js.map +1 -0
  243. package/dist/dispatch/runner.d.ts +76 -0
  244. package/dist/dispatch/runner.js +415 -0
  245. package/dist/dispatch/runner.js.map +1 -0
  246. package/dist/dispatch/utils.d.ts +159 -0
  247. package/dist/dispatch/utils.js +381 -0
  248. package/dist/dispatch/utils.js.map +1 -0
  249. package/dist/dispatch/verification-execution.d.ts +48 -0
  250. package/dist/dispatch/verification-execution.js +252 -0
  251. package/dist/dispatch/verification-execution.js.map +1 -0
  252. package/dist/dispatch/verification-late-audit.d.ts +17 -0
  253. package/dist/dispatch/verification-late-audit.js +117 -0
  254. package/dist/dispatch/verification-late-audit.js.map +1 -0
  255. package/dist/evidence/claim-recovery/authenticated-carrier-projection.d.ts +30 -0
  256. package/dist/evidence/claim-recovery/authenticated-carrier-projection.js +100 -0
  257. package/dist/evidence/claim-recovery/authenticated-carrier-projection.js.map +1 -0
  258. package/dist/evidence/claim-recovery/authenticated-receipts.d.ts +46 -0
  259. package/dist/evidence/claim-recovery/authenticated-receipts.js +120 -0
  260. package/dist/evidence/claim-recovery/authenticated-receipts.js.map +1 -0
  261. package/dist/evidence/claim-recovery/carrier-inventory.d.ts +49 -0
  262. package/dist/evidence/claim-recovery/carrier-inventory.js +340 -0
  263. package/dist/evidence/claim-recovery/carrier-inventory.js.map +1 -0
  264. package/dist/evidence/claim-recovery/claim-index.d.ts +12 -0
  265. package/dist/evidence/claim-recovery/claim-index.js +56 -0
  266. package/dist/evidence/claim-recovery/claim-index.js.map +1 -0
  267. package/dist/evidence/claim-recovery/confirmation.d.ts +53 -0
  268. package/dist/evidence/claim-recovery/confirmation.js +151 -0
  269. package/dist/evidence/claim-recovery/confirmation.js.map +1 -0
  270. package/dist/evidence/claim-recovery/context.d.ts +15 -0
  271. package/dist/evidence/claim-recovery/context.js +40 -0
  272. package/dist/evidence/claim-recovery/context.js.map +1 -0
  273. package/dist/evidence/claim-recovery/proof-storage.d.ts +6 -0
  274. package/dist/evidence/claim-recovery/proof-storage.js +97 -0
  275. package/dist/evidence/claim-recovery/proof-storage.js.map +1 -0
  276. package/dist/evidence/claim-recovery/validation/anchors.d.ts +20 -0
  277. package/dist/evidence/claim-recovery/validation/anchors.js +22 -0
  278. package/dist/evidence/claim-recovery/validation/anchors.js.map +1 -0
  279. package/dist/evidence/claim-recovery/validation/carrier-projection.d.ts +9 -0
  280. package/dist/evidence/claim-recovery/validation/carrier-projection.js +364 -0
  281. package/dist/evidence/claim-recovery/validation/carrier-projection.js.map +1 -0
  282. package/dist/evidence/claim-recovery/validation/carrier-types.d.ts +83 -0
  283. package/dist/evidence/claim-recovery/validation/carrier-types.js +2 -0
  284. package/dist/evidence/claim-recovery/validation/carrier-types.js.map +1 -0
  285. package/dist/evidence/claim-recovery/validation/claim-split.d.ts +57 -0
  286. package/dist/evidence/claim-recovery/validation/claim-split.js +209 -0
  287. package/dist/evidence/claim-recovery/validation/claim-split.js.map +1 -0
  288. package/dist/evidence/claim-recovery/validation/claims.d.ts +20 -0
  289. package/dist/evidence/claim-recovery/validation/claims.js +108 -0
  290. package/dist/evidence/claim-recovery/validation/claims.js.map +1 -0
  291. package/dist/evidence/claim-recovery/validation/current-projection.d.ts +36 -0
  292. package/dist/evidence/claim-recovery/validation/current-projection.js +424 -0
  293. package/dist/evidence/claim-recovery/validation/current-projection.js.map +1 -0
  294. package/dist/evidence/claim-recovery/validation/lexical.d.ts +18 -0
  295. package/dist/evidence/claim-recovery/validation/lexical.js +91 -0
  296. package/dist/evidence/claim-recovery/validation/lexical.js.map +1 -0
  297. package/dist/evidence/claim-recovery/validation/materials.d.ts +19 -0
  298. package/dist/evidence/claim-recovery/validation/materials.js +160 -0
  299. package/dist/evidence/claim-recovery/validation/materials.js.map +1 -0
  300. package/dist/evidence/claim-recovery/validation/native-material.d.ts +27 -0
  301. package/dist/evidence/claim-recovery/validation/native-material.js +35 -0
  302. package/dist/evidence/claim-recovery/validation/native-material.js.map +1 -0
  303. package/dist/evidence/claim-recovery/validation/native-occurrences.d.ts +79 -0
  304. package/dist/evidence/claim-recovery/validation/native-occurrences.js +187 -0
  305. package/dist/evidence/claim-recovery/validation/native-occurrences.js.map +1 -0
  306. package/dist/evidence/claim-recovery/validation/native-state.d.ts +45 -0
  307. package/dist/evidence/claim-recovery/validation/native-state.js +385 -0
  308. package/dist/evidence/claim-recovery/validation/native-state.js.map +1 -0
  309. package/dist/evidence/claim-recovery/validation/obligations.d.ts +15 -0
  310. package/dist/evidence/claim-recovery/validation/obligations.js +65 -0
  311. package/dist/evidence/claim-recovery/validation/obligations.js.map +1 -0
  312. package/dist/evidence/claim-recovery/validation/occurrence-source.d.ts +45 -0
  313. package/dist/evidence/claim-recovery/validation/occurrence-source.js +208 -0
  314. package/dist/evidence/claim-recovery/validation/occurrence-source.js.map +1 -0
  315. package/dist/evidence/claim-recovery/validation/occurrence-types.d.ts +101 -0
  316. package/dist/evidence/claim-recovery/validation/occurrence-types.js +2 -0
  317. package/dist/evidence/claim-recovery/validation/occurrence-types.js.map +1 -0
  318. package/dist/evidence/claim-recovery/validation/occurrence.d.ts +30 -0
  319. package/dist/evidence/claim-recovery/validation/occurrence.js +227 -0
  320. package/dist/evidence/claim-recovery/validation/occurrence.js.map +1 -0
  321. package/dist/evidence/claim-recovery/validation/primitives.d.ts +11 -0
  322. package/dist/evidence/claim-recovery/validation/primitives.js +41 -0
  323. package/dist/evidence/claim-recovery/validation/primitives.js.map +1 -0
  324. package/dist/evidence/claim-recovery/validation/receipts.d.ts +37 -0
  325. package/dist/evidence/claim-recovery/validation/receipts.js +83 -0
  326. package/dist/evidence/claim-recovery/validation/receipts.js.map +1 -0
  327. package/dist/evidence/claim-recovery/validation/recovery-json.d.ts +8 -0
  328. package/dist/evidence/claim-recovery/validation/recovery-json.js +42 -0
  329. package/dist/evidence/claim-recovery/validation/recovery-json.js.map +1 -0
  330. package/dist/evidence/claim-recovery/validation/semantic-cache.d.ts +3 -0
  331. package/dist/evidence/claim-recovery/validation/semantic-cache.js +22 -0
  332. package/dist/evidence/claim-recovery/validation/semantic-cache.js.map +1 -0
  333. package/dist/evidence/claim-recovery/validation/semantic-validation.d.ts +9 -0
  334. package/dist/evidence/claim-recovery/validation/semantic-validation.js +347 -0
  335. package/dist/evidence/claim-recovery/validation/semantic-validation.js.map +1 -0
  336. package/dist/evidence/claim-recovery/validation/sighting.d.ts +2 -0
  337. package/dist/evidence/claim-recovery/validation/sighting.js +15 -0
  338. package/dist/evidence/claim-recovery/validation/sighting.js.map +1 -0
  339. package/dist/evidence/claim-recovery/validation/sources.d.ts +15 -0
  340. package/dist/evidence/claim-recovery/validation/sources.js +2 -0
  341. package/dist/evidence/claim-recovery/validation/sources.js.map +1 -0
  342. package/dist/evidence/claim-recovery/validation/types.d.ts +126 -0
  343. package/dist/evidence/claim-recovery/validation/types.js +2 -0
  344. package/dist/evidence/claim-recovery/validation/types.js.map +1 -0
  345. package/dist/evidence/claim-split.d.ts +1 -0
  346. package/dist/evidence/claim-split.js +2 -0
  347. package/dist/evidence/claim-split.js.map +1 -0
  348. package/dist/evidence/event-receipts.d.ts +16 -0
  349. package/dist/evidence/event-receipts.js +41 -0
  350. package/dist/evidence/event-receipts.js.map +1 -0
  351. package/dist/evidence/finding-recovery.d.ts +23 -0
  352. package/dist/evidence/finding-recovery.js +74 -0
  353. package/dist/evidence/finding-recovery.js.map +1 -0
  354. package/dist/evidence/finding-retriage.d.ts +14 -0
  355. package/dist/evidence/finding-retriage.js +57 -0
  356. package/dist/evidence/finding-retriage.js.map +1 -0
  357. package/dist/evidence/format.d.ts +18 -0
  358. package/dist/evidence/format.js +152 -0
  359. package/dist/evidence/format.js.map +1 -0
  360. package/dist/evidence/original-run/decode.d.ts +30 -0
  361. package/dist/evidence/original-run/decode.js +208 -0
  362. package/dist/evidence/original-run/decode.js.map +1 -0
  363. package/dist/evidence/original-run/journal.d.ts +31 -0
  364. package/dist/evidence/original-run/journal.js +157 -0
  365. package/dist/evidence/original-run/journal.js.map +1 -0
  366. package/dist/evidence/original-run/lock-path.d.ts +17 -0
  367. package/dist/evidence/original-run/lock-path.js +116 -0
  368. package/dist/evidence/original-run/lock-path.js.map +1 -0
  369. package/dist/evidence/original-run/lock-scope.d.ts +21 -0
  370. package/dist/evidence/original-run/lock-scope.js +61 -0
  371. package/dist/evidence/original-run/lock-scope.js.map +1 -0
  372. package/dist/evidence/original-run/lock.d.ts +33 -0
  373. package/dist/evidence/original-run/lock.js +259 -0
  374. package/dist/evidence/original-run/lock.js.map +1 -0
  375. package/dist/evidence/original-run/remote.d.ts +23 -0
  376. package/dist/evidence/original-run/remote.js +110 -0
  377. package/dist/evidence/original-run/remote.js.map +1 -0
  378. package/dist/evidence/original-run/source.d.ts +61 -0
  379. package/dist/evidence/original-run/source.js +267 -0
  380. package/dist/evidence/original-run/source.js.map +1 -0
  381. package/dist/evidence/reads.d.ts +11 -0
  382. package/dist/evidence/reads.js +16 -0
  383. package/dist/evidence/reads.js.map +1 -0
  384. package/dist/evidence/recover-finding.d.ts +17 -0
  385. package/dist/evidence/recover-finding.js +65 -0
  386. package/dist/evidence/recover-finding.js.map +1 -0
  387. package/dist/evidence/recover-run.d.ts +26 -0
  388. package/dist/evidence/recover-run.js +180 -0
  389. package/dist/evidence/recover-run.js.map +1 -0
  390. package/dist/evidence/retained-time-budget.d.ts +9 -0
  391. package/dist/evidence/retained-time-budget.js +14 -0
  392. package/dist/evidence/retained-time-budget.js.map +1 -0
  393. package/dist/evidence/retriage-finding.d.ts +12 -0
  394. package/dist/evidence/retriage-finding.js +67 -0
  395. package/dist/evidence/retriage-finding.js.map +1 -0
  396. package/dist/evidence/reviewer-lineage.d.ts +35 -0
  397. package/dist/evidence/reviewer-lineage.js +293 -0
  398. package/dist/evidence/reviewer-lineage.js.map +1 -0
  399. package/dist/evidence/reviewer-original.d.ts +67 -0
  400. package/dist/evidence/reviewer-original.js +148 -0
  401. package/dist/evidence/reviewer-original.js.map +1 -0
  402. package/dist/evidence/reviewer-recovery.d.ts +74 -0
  403. package/dist/evidence/reviewer-recovery.js +179 -0
  404. package/dist/evidence/reviewer-recovery.js.map +1 -0
  405. package/dist/evidence/reviewer-status.d.ts +145 -0
  406. package/dist/evidence/reviewer-status.js +302 -0
  407. package/dist/evidence/reviewer-status.js.map +1 -0
  408. package/dist/evidence/show.d.ts +10 -0
  409. package/dist/evidence/show.js +41 -0
  410. package/dist/evidence/show.js.map +1 -0
  411. package/dist/evidence/status.d.ts +38 -0
  412. package/dist/evidence/status.js +73 -0
  413. package/dist/evidence/status.js.map +1 -0
  414. package/dist/evidence/target.d.ts +17 -0
  415. package/dist/evidence/target.js +74 -0
  416. package/dist/evidence/target.js.map +1 -0
  417. package/dist/evidence/types.d.ts +176 -0
  418. package/dist/evidence/types.js +109 -0
  419. package/dist/evidence/types.js.map +1 -0
  420. package/dist/index.d.ts +2 -0
  421. package/dist/index.js +2688 -0
  422. package/dist/index.js.map +1 -0
  423. package/dist/models/seed.d.ts +44 -0
  424. package/dist/models/seed.js +175 -0
  425. package/dist/models/seed.js.map +1 -0
  426. package/dist/models/server-stats.d.ts +80 -0
  427. package/dist/models/server-stats.js +109 -0
  428. package/dist/models/server-stats.js.map +1 -0
  429. package/dist/models/stats-store.d.ts +84 -0
  430. package/dist/models/stats-store.js +572 -0
  431. package/dist/models/stats-store.js.map +1 -0
  432. package/dist/output/artifacts.d.ts +16 -0
  433. package/dist/output/artifacts.js +29 -0
  434. package/dist/output/artifacts.js.map +1 -0
  435. package/dist/output/assembly-refusal.d.ts +8 -0
  436. package/dist/output/assembly-refusal.js +14 -0
  437. package/dist/output/assembly-refusal.js.map +1 -0
  438. package/dist/output/github.d.ts +12 -0
  439. package/dist/output/github.js +214 -0
  440. package/dist/output/github.js.map +1 -0
  441. package/dist/output/json.d.ts +3 -0
  442. package/dist/output/json.js +8 -0
  443. package/dist/output/json.js.map +1 -0
  444. package/dist/output/markdown.d.ts +3 -0
  445. package/dist/output/markdown.js +281 -0
  446. package/dist/output/markdown.js.map +1 -0
  447. package/dist/output/progress.d.ts +55 -0
  448. package/dist/output/progress.js +180 -0
  449. package/dist/output/progress.js.map +1 -0
  450. package/dist/output/sanitize.d.ts +18 -0
  451. package/dist/output/sanitize.js +46 -0
  452. package/dist/output/sanitize.js.map +1 -0
  453. package/dist/output/terminal.d.ts +2 -0
  454. package/dist/output/terminal.js +172 -0
  455. package/dist/output/terminal.js.map +1 -0
  456. package/dist/prepare/chunker.d.ts +18 -0
  457. package/dist/prepare/chunker.js +325 -0
  458. package/dist/prepare/chunker.js.map +1 -0
  459. package/dist/prepare/language.d.ts +1 -0
  460. package/dist/prepare/language.js +99 -0
  461. package/dist/prepare/language.js.map +1 -0
  462. package/dist/prepare/prompt-builder.d.ts +44 -0
  463. package/dist/prepare/prompt-builder.js +102 -0
  464. package/dist/prepare/prompt-builder.js.map +1 -0
  465. package/dist/prepare/unified-diff.d.ts +40 -0
  466. package/dist/prepare/unified-diff.js +135 -0
  467. package/dist/prepare/unified-diff.js.map +1 -0
  468. package/dist/prompts/base.d.ts +3 -0
  469. package/dist/prompts/base.js +53 -0
  470. package/dist/prompts/base.js.map +1 -0
  471. package/dist/prompts/hardening.d.ts +19 -0
  472. package/dist/prompts/hardening.js +67 -0
  473. package/dist/prompts/hardening.js.map +1 -0
  474. package/dist/prompts/languages/elixir.d.ts +1 -0
  475. package/dist/prompts/languages/elixir.js +13 -0
  476. package/dist/prompts/languages/elixir.js.map +1 -0
  477. package/dist/prompts/languages/generic.d.ts +1 -0
  478. package/dist/prompts/languages/generic.js +11 -0
  479. package/dist/prompts/languages/generic.js.map +1 -0
  480. package/dist/prompts/languages/python.d.ts +1 -0
  481. package/dist/prompts/languages/python.js +14 -0
  482. package/dist/prompts/languages/python.js.map +1 -0
  483. package/dist/prompts/languages/typescript.d.ts +1 -0
  484. package/dist/prompts/languages/typescript.js +13 -0
  485. package/dist/prompts/languages/typescript.js.map +1 -0
  486. package/dist/prompts/plan.d.ts +15 -0
  487. package/dist/prompts/plan.js +55 -0
  488. package/dist/prompts/plan.js.map +1 -0
  489. package/dist/report/aggregation-inputs.d.ts +52 -0
  490. package/dist/report/aggregation-inputs.js +114 -0
  491. package/dist/report/aggregation-inputs.js.map +1 -0
  492. package/dist/report/assembly.d.ts +43 -0
  493. package/dist/report/assembly.js +133 -0
  494. package/dist/report/assembly.js.map +1 -0
  495. package/dist/report/blocking-health.d.ts +64 -0
  496. package/dist/report/blocking-health.js +156 -0
  497. package/dist/report/blocking-health.js.map +1 -0
  498. package/dist/report/checkpoint-assembly.d.ts +10 -0
  499. package/dist/report/checkpoint-assembly.js +17 -0
  500. package/dist/report/checkpoint-assembly.js.map +1 -0
  501. package/dist/report/checkpoint-consensus.d.ts +59 -0
  502. package/dist/report/checkpoint-consensus.js +197 -0
  503. package/dist/report/checkpoint-consensus.js.map +1 -0
  504. package/dist/report/checkpoint-gating.d.ts +36 -0
  505. package/dist/report/checkpoint-gating.js +110 -0
  506. package/dist/report/checkpoint-gating.js.map +1 -0
  507. package/dist/report/checkpoint-projection.d.ts +76 -0
  508. package/dist/report/checkpoint-projection.js +122 -0
  509. package/dist/report/checkpoint-projection.js.map +1 -0
  510. package/dist/report/consensus-assembly.d.ts +37 -0
  511. package/dist/report/consensus-assembly.js +33 -0
  512. package/dist/report/consensus-assembly.js.map +1 -0
  513. package/dist/report/recovery-source.d.ts +7 -0
  514. package/dist/report/recovery-source.js +7 -0
  515. package/dist/report/recovery-source.js.map +1 -0
  516. package/dist/report/reviewer-artifact.d.ts +118 -0
  517. package/dist/report/reviewer-artifact.js +381 -0
  518. package/dist/report/reviewer-artifact.js.map +1 -0
  519. package/dist/report/reviewer-evidence-schema.d.ts +42 -0
  520. package/dist/report/reviewer-evidence-schema.js +28 -0
  521. package/dist/report/reviewer-evidence-schema.js.map +1 -0
  522. package/dist/report/reviewer-evidence.d.ts +54 -0
  523. package/dist/report/reviewer-evidence.js +229 -0
  524. package/dist/report/reviewer-evidence.js.map +1 -0
  525. package/dist/report/reviewer-health.d.ts +62 -0
  526. package/dist/report/reviewer-health.js +105 -0
  527. package/dist/report/reviewer-health.js.map +1 -0
  528. package/dist/report/run-header.d.ts +219 -0
  529. package/dist/report/run-header.js +307 -0
  530. package/dist/report/run-header.js.map +1 -0
  531. package/dist/report/supplemental-async.d.ts +19 -0
  532. package/dist/report/supplemental-async.js +85 -0
  533. package/dist/report/supplemental-async.js.map +1 -0
  534. package/dist/report/uuid.d.ts +16 -0
  535. package/dist/report/uuid.js +41 -0
  536. package/dist/report/uuid.js.map +1 -0
  537. package/dist/resolver/git.d.ts +16 -0
  538. package/dist/resolver/git.js +77 -0
  539. package/dist/resolver/git.js.map +1 -0
  540. package/dist/resolver/github-client.d.ts +82 -0
  541. package/dist/resolver/github-client.js +27 -0
  542. package/dist/resolver/github-client.js.map +1 -0
  543. package/dist/resolver/github.d.ts +29 -0
  544. package/dist/resolver/github.js +166 -0
  545. package/dist/resolver/github.js.map +1 -0
  546. package/dist/resolver/local.d.ts +3 -0
  547. package/dist/resolver/local.js +75 -0
  548. package/dist/resolver/local.js.map +1 -0
  549. package/dist/resolver/plan.d.ts +8 -0
  550. package/dist/resolver/plan.js +44 -0
  551. package/dist/resolver/plan.js.map +1 -0
  552. package/dist/resolver/target.d.ts +35 -0
  553. package/dist/resolver/target.js +112 -0
  554. package/dist/resolver/target.js.map +1 -0
  555. package/dist/resolver/types.d.ts +43 -0
  556. package/dist/resolver/types.js +2 -0
  557. package/dist/resolver/types.js.map +1 -0
  558. package/dist/roles/builtin.d.ts +4 -0
  559. package/dist/roles/builtin.js +282 -0
  560. package/dist/roles/builtin.js.map +1 -0
  561. package/dist/roles/dispatcher.d.ts +26 -0
  562. package/dist/roles/dispatcher.js +96 -0
  563. package/dist/roles/dispatcher.js.map +1 -0
  564. package/dist/roles/legacy-4.1.10.d.ts +2 -0
  565. package/dist/roles/legacy-4.1.10.js +320 -0
  566. package/dist/roles/legacy-4.1.10.js.map +1 -0
  567. package/dist/roles/legacy-4.4.9.d.ts +4 -0
  568. package/dist/roles/legacy-4.4.9.js +282 -0
  569. package/dist/roles/legacy-4.4.9.js.map +1 -0
  570. package/dist/roles/loader.d.ts +10 -0
  571. package/dist/roles/loader.js +104 -0
  572. package/dist/roles/loader.js.map +1 -0
  573. package/dist/roles/types.d.ts +13 -0
  574. package/dist/roles/types.js +2 -0
  575. package/dist/roles/types.js.map +1 -0
  576. package/dist/telemetry/abort-signal.d.ts +13 -0
  577. package/dist/telemetry/abort-signal.js +40 -0
  578. package/dist/telemetry/abort-signal.js.map +1 -0
  579. package/dist/telemetry/attest.d.ts +98 -0
  580. package/dist/telemetry/attest.js +331 -0
  581. package/dist/telemetry/attest.js.map +1 -0
  582. package/dist/telemetry/attested-retry.d.ts +79 -0
  583. package/dist/telemetry/attested-retry.js +195 -0
  584. package/dist/telemetry/attested-retry.js.map +1 -0
  585. package/dist/telemetry/attested-reviewer-delivery.d.ts +58 -0
  586. package/dist/telemetry/attested-reviewer-delivery.js +238 -0
  587. package/dist/telemetry/attested-reviewer-delivery.js.map +1 -0
  588. package/dist/telemetry/backfill.d.ts +95 -0
  589. package/dist/telemetry/backfill.js +460 -0
  590. package/dist/telemetry/backfill.js.map +1 -0
  591. package/dist/telemetry/credentials.d.ts +44 -0
  592. package/dist/telemetry/credentials.js +87 -0
  593. package/dist/telemetry/credentials.js.map +1 -0
  594. package/dist/telemetry/deliver.d.ts +152 -0
  595. package/dist/telemetry/deliver.js +662 -0
  596. package/dist/telemetry/deliver.js.map +1 -0
  597. package/dist/telemetry/envelope-validation.d.ts +13 -0
  598. package/dist/telemetry/envelope-validation.js +196 -0
  599. package/dist/telemetry/envelope-validation.js.map +1 -0
  600. package/dist/telemetry/envelope.d.ts +159 -0
  601. package/dist/telemetry/envelope.js +288 -0
  602. package/dist/telemetry/envelope.js.map +1 -0
  603. package/dist/telemetry/events.d.ts +68 -0
  604. package/dist/telemetry/events.js +79 -0
  605. package/dist/telemetry/events.js.map +1 -0
  606. package/dist/telemetry/notice.d.ts +15 -0
  607. package/dist/telemetry/notice.js +67 -0
  608. package/dist/telemetry/notice.js.map +1 -0
  609. package/dist/telemetry/outbox.d.ts +166 -0
  610. package/dist/telemetry/outbox.js +741 -0
  611. package/dist/telemetry/outbox.js.map +1 -0
  612. package/dist/telemetry/quarantine.d.ts +105 -0
  613. package/dist/telemetry/quarantine.js +224 -0
  614. package/dist/telemetry/quarantine.js.map +1 -0
  615. package/dist/telemetry/read-sink.d.ts +31 -0
  616. package/dist/telemetry/read-sink.js +25 -0
  617. package/dist/telemetry/read-sink.js.map +1 -0
  618. package/dist/telemetry/recovery/command.d.ts +16 -0
  619. package/dist/telemetry/recovery/command.js +70 -0
  620. package/dist/telemetry/recovery/command.js.map +1 -0
  621. package/dist/telemetry/recovery/discovery.d.ts +8 -0
  622. package/dist/telemetry/recovery/discovery.js +277 -0
  623. package/dist/telemetry/recovery/discovery.js.map +1 -0
  624. package/dist/telemetry/recovery/files.d.ts +20 -0
  625. package/dist/telemetry/recovery/files.js +123 -0
  626. package/dist/telemetry/recovery/files.js.map +1 -0
  627. package/dist/telemetry/recovery/plan.d.ts +10 -0
  628. package/dist/telemetry/recovery/plan.js +325 -0
  629. package/dist/telemetry/recovery/plan.js.map +1 -0
  630. package/dist/telemetry/recovery/source.d.ts +328 -0
  631. package/dist/telemetry/recovery/source.js +253 -0
  632. package/dist/telemetry/recovery/source.js.map +1 -0
  633. package/dist/telemetry/recovery/types.d.ts +100 -0
  634. package/dist/telemetry/recovery/types.js +2 -0
  635. package/dist/telemetry/recovery/types.js.map +1 -0
  636. package/dist/telemetry/recovery-request-budget.d.ts +39 -0
  637. package/dist/telemetry/recovery-request-budget.js +102 -0
  638. package/dist/telemetry/recovery-request-budget.js.map +1 -0
  639. package/dist/telemetry/report-consistency.d.ts +11 -0
  640. package/dist/telemetry/report-consistency.js +111 -0
  641. package/dist/telemetry/report-consistency.js.map +1 -0
  642. package/dist/telemetry/reviewer-delivery.d.ts +39 -0
  643. package/dist/telemetry/reviewer-delivery.js +279 -0
  644. package/dist/telemetry/reviewer-delivery.js.map +1 -0
  645. package/dist/telemetry/reviewer-preflight.d.ts +13 -0
  646. package/dist/telemetry/reviewer-preflight.js +56 -0
  647. package/dist/telemetry/reviewer-preflight.js.map +1 -0
  648. package/dist/telemetry/scrub.d.ts +53 -0
  649. package/dist/telemetry/scrub.js +205 -0
  650. package/dist/telemetry/scrub.js.map +1 -0
  651. package/dist/telemetry/sink.d.ts +212 -0
  652. package/dist/telemetry/sink.js +813 -0
  653. package/dist/telemetry/sink.js.map +1 -0
  654. package/dist/telemetry/terminal-reviewer-delivery.d.ts +18 -0
  655. package/dist/telemetry/terminal-reviewer-delivery.js +27 -0
  656. package/dist/telemetry/terminal-reviewer-delivery.js.map +1 -0
  657. package/dist/telemetry/transient-input.d.ts +2 -0
  658. package/dist/telemetry/transient-input.js +37 -0
  659. package/dist/telemetry/transient-input.js.map +1 -0
  660. package/dist/telemetry/verification.d.ts +9 -0
  661. package/dist/telemetry/verification.js +12 -0
  662. package/dist/telemetry/verification.js.map +1 -0
  663. package/package.json +73 -0
package/README.md ADDED
@@ -0,0 +1,1415 @@
1
+ # review-council
2
+
3
+ > Multi-model AI code review in your terminal — many models, many roles, one consensus.
4
+
5
+ ![npm](https://img.shields.io/npm/v/review-council) ![license](https://img.shields.io/npm/l/review-council) ![node](https://img.shields.io/node/v/review-council)
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g review-council
13
+ ```
14
+
15
+ Requires Node.js >= 18.
16
+
17
+ ---
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Review a GitHub PR with default models and roles
23
+ rcl review owner/repo#42
24
+
25
+ # Review with specific roles and post findings as a PR comment
26
+ rcl review owner/repo#42 --roles security-auditor,bug-hunter --post
27
+
28
+ # Review a local patch file; fail CI if critical/important findings exist
29
+ rcl review changes.patch --ci --markdown report.md
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Built-in Roles
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 |
51
+
52
+ `project-rules` and `dead-code` are no longer built-in reviewer roles. Repository
53
+ rules discovered in `AGENTS.md`, `CLAUDE.md`, or the other supported rules files
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.
57
+
58
+ The default Opus 5.5 and Sol general reviewers plus specialist assignments schedule
59
+ 13 blocking seats, or 14 when a specification enables `spec-compliance`. Gemini
60
+ is eligible for specialist assignments only. The async general reviewer
61
+ and verifier are separate; quorum is calculated from the blocking roster.
62
+
63
+ List roles in the terminal:
64
+
65
+ ```bash
66
+ rcl roles list
67
+ rcl roles show security-auditor
68
+ ```
69
+
70
+ ---
71
+
72
+ ## CLI Reference
73
+
74
+ ### `rcl review [target]`
75
+
76
+ Review a PR, a local diff, or uncommitted work.
77
+
78
+ **Target formats:**
79
+ - `owner/repo#N` — GitHub PR number
80
+ - GitHub PR URL
81
+ - Path to a `.patch` or `.diff` file
82
+ - No target with `--staged` or `--working-tree` — review uncommitted changes in the current repository
83
+
84
+ **Options:**
85
+
86
+ | Flag | Description |
87
+ |------|-------------|
88
+ | `--staged` | Review staged changes (`git diff --cached`) |
89
+ | `--working-tree` | Review all uncommitted changes (`git diff HEAD`, staged + unstaged) |
90
+ | `--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 list of models to use |
94
+ | `--context <path>` | Context file or directory (repeatable) |
95
+ | `--spec <path>` | Specification file for `spec-compliance` role |
96
+ | `--focus <areas>` | Comma-separated focus areas |
97
+ | `--post` | Post review as a GitHub PR comment |
98
+ | `--json` | Print JSON output to stdout |
99
+ | `--json-file <path>` | Write JSON output to a file |
100
+ | `--markdown <path>` | Write Markdown report to a file |
101
+ | `--ci` | Exit non-zero if critical/important findings exist |
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) |
104
+ | `--expect-head-sha <sha>` | Fail fast unless the resolved head commit equals this SHA |
105
+ | `--spec-source <source>` | Where `--spec` came from: `flag`, `repo_file`, or `harness_issue:<ID>` |
106
+ | `--converge-target <key>` / `--round <n>` / `--attempt <n>` | Converge context recorded in the report (or `RCL_CONVERGE_TARGET` / `_ROUND` / `_ATTEMPT`) |
107
+ | `--start-over` | Explicitly start a fresh PR review with a new normal budget; preserve all prior spending and evidence |
108
+ | `--guarded-converge` | Validate and claim inside this process; derive the next round from native admitted state |
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) |
118
+ | `--config <path>` | Path to a config file |
119
+
120
+ `--role`, `--roles`, and `--reviewer` are mutually exclusive. So are a positional target, `--staged`, and `--working-tree` — pick exactly one review source. Untracked files are invisible to `git diff` and therefore not reviewed.
121
+
122
+ **Self-describing reports (3.0).** Every report carries a `run` header: a client run id (UUIDv7), the rcl version, the target with its exact `head_sha`/`base_sha` (from GitHub for PRs, from `git rev-parse HEAD` and the merge-base with the remote default branch for `--staged`/`--working-tree`, from `--head-sha`/`--base-sha` for patch files) and a `diff_sha256`, the roster with each seat's lane (`blocking`, `secondary`, `async`, `verification`), a config digest with thresholds and gating inline, spec and context-file digests, a best-effort `runner` claim (`agent` / `ci` / `human`), timing, the CI verdict (computed even without `--ci`), and the converge context when run under rcl-converge. Every finding carries an `identity`, allocated uniquely across the report's consensus findings, including the below-threshold appendix. Keys use `report:<run-id>:<16-hex-key>` so report allocation cannot alias unrelated native ledger identities or reuse another run's classification. Colliding location anchors are disambiguated before thresholding; native cross-round location matching still determines the unchanged canonical ledger identity. Every reviewer call records token `usage` where the provider reports it. Reports without a `run` header (pre-3.0) remain readable; ambiguous classifications are refused as described below.
123
+
124
+ **Examples:**
125
+
126
+ ```bash
127
+ # Use explicit model:role pairs
128
+ rcl review owner/repo#7 \
129
+ --reviewer claude-opus-5-5:security-auditor \
130
+ --reviewer gpt-6-sol:bug-hunter
131
+
132
+ # Spec compliance review with context
133
+ rcl review ./feature.patch --role spec-compliance --spec SPEC.md --context src/
134
+
135
+ # Output JSON for downstream processing
136
+ rcl review owner/repo#99 --json > findings.json
137
+
138
+ # Review your uncommitted work before committing
139
+ rcl review --staged
140
+ rcl review --working-tree --roles security-auditor,bug-hunter
141
+ ```
142
+
143
+ ---
144
+
145
+ ### `rcl review-plan <file>`
146
+
147
+ Council-review an implementation plan document (PRD, BUILD_PLAN.md, design doc) **before any code exists** — the cheapest bugs to fix are the ones caught in the plan.
148
+
149
+ ```bash
150
+ rcl review-plan docs/plan.md
151
+ rcl review-plan docs/plan.md --focus risks # feasibility | completeness | risks | timeline
152
+ rcl review-plan docs/plan.md --spec PRD.md # also check the plan against a spec
153
+ ```
154
+
155
+ The plan flows through the normal pipeline — multi-model dispatch, dedup, consensus, agreement-tier report — with plan-adapted prompts. Finding line numbers refer to the plan document's own lines. Categories are reinterpreted for plans (`correctness` = infeasible/contradictory steps, `tests` = missing validation strategy, `best-practices` = process gaps like rollback/migration, …).
156
+
157
+ Default roles are a plan-suited subset (`general`, `architecture`, `edge-case-hunter`, plus `spec-compliance` when a spec is given); `--role`/`--roles`/`--reviewer` and config `roles` override as usual. Shares `--context`, `--models`, `--json`, `--json-file`, `--markdown`, and `--config` with `rcl review`. `--post` and `--ci` are not offered (no PR to post to; plan findings are judgment calls, not gates).
158
+
159
+ ---
160
+
161
+ ### `rcl discuss`
162
+
163
+ Ask the models that flagged a finding a follow-up question — one round, reconstructed from a saved report. Useful when triaging: "is this actually exploitable given the sanitizer at line 40?" goes to the reviewers who raised it (especially valuable for **disputed** findings, where the report shows each model's position).
164
+
165
+ ```bash
166
+ rcl review --staged --json-file report.json
167
+ rcl discuss --report report.json --finding f003 "Is this exploitable given the sanitizer at line 40?"
168
+
169
+ # Attach code as context, or ask different models
170
+ rcl discuss --report report.json --finding f003 --context src/auth.ts "Does the middleware at line 12 not already cover this?"
171
+ rcl discuss --report report.json --finding f003 --models anthropic/claude-opus-5-5 "Summarize the strongest counterargument."
172
+ ```
173
+
174
+ Model-generated finding ids can collide; when `--finding <id>` is ambiguous the error lists `<id>:<n>` disambiguators. Findings in the below-threshold appendix are addressable too. Answers come back in parallel, respecting the configured `timeout`, `maxRetries`, and `reasoningEffort`. There is no session state: each `discuss` is one independent round built from the report file.
175
+
176
+ ---
177
+
178
+ ### `rcl roles`
179
+
180
+ ```bash
181
+ rcl roles list # List all built-in roles
182
+ rcl roles show <name> # Show system prompt and details for a role
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
662
+ ```
663
+
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 |
675
+ | --- | --- |
676
+ | 0 | the judged projection (advisory by default, `--enforced` on request) is `converged` |
677
+ | 1 | any other status: `none`, `stale`, `unverified`, `inconclusive`, `fixes_pending`, `unresolved` |
678
+ | 2 | the pull request could not be named |
679
+ | 3 | the read could not be answered: no credential, evidence off for the organization, unknown pull request, refused credential, unreachable host — never reported as "not converged" |
680
+
681
+ `rcl evidence show <run id>` prints one recorded run: header and verification,
682
+ credential tier and runner, reviewer health, artifact state, and every finding
683
+ with its identity and gating reason. Each **Verification** block shows the
684
+ recorded result, actual model and complete stored explanation. Recovered notes
685
+ identify the original report digest and recovery time. Legacy results without
686
+ a note say `Explanation not recorded`; `unavailable` remains distinct from
687
+ `refuted`. A separate **Triage** block shows the recorded judgment, reason,
688
+ actor, round and recording time when available. Missing attribution stays
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.
975
+
976
+ ```bash
977
+ rcl review round-3.patch --head-sha "$HEAD" --base-sha "$BASE" --for-pr allocator-one/rcl#42 \
978
+ --converge-target allocator-one/rcl#42 --round 3 --attempt 3 --evidence-required
979
+ ```
980
+
981
+ ### `rcl models`
982
+
983
+ The tool's own memory of which reviewers earn their seat. Every reviewer call
984
+ and every `converge-verdict` outcome accrues in a cross-run store at `~/.rcl`
985
+ (`RCL_DATA_DIR` overrides; deliberately not under /tmp, so history survives
986
+ converge-state cleanup). `rcl models` prints, per model over a trailing 90-day
987
+ window: triage precision (share of its supported findings the converge loop
988
+ verified and fixed rather than dismissed), triage volume, call volume,
989
+ dead-call rate, p50 latency — and the consensus **weight** the model earns:
990
+ `0.5 + precision`, clamped to [0.5, 1.5], neutral (1) below 20 triaged
991
+ outcomes. Weights scale each model's consensus vote in report confidence and
992
+ in consensus gating, so persistently noisy models lose gating power
993
+ automatically; the applied weights are visible per finding
994
+ (`consensus.weightedScore` / `consensus.modelWeights`) and per run
995
+ (`stats.modelWeights`) in the report JSON.
996
+
997
+ ```bash
998
+ rcl models # table over the trailing 90 days
999
+ rcl models show --window 30 --json
1000
+ rcl models seed --from ~/recovered-rcl-artifacts # backfill from reports + converge ledgers
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.
1036
+
1037
+ ```bash
1038
+ rcl telemetry backfill --from ~/recovered-rcl-artifacts --repo allocator-one/allocator-one --dry-run
1039
+ rcl telemetry backfill --from ~/recovered-rcl-artifacts --repo allocator-one/allocator-one
1040
+ ```
1041
+
1042
+ ### `rcl telemetry recover-refutations`
1043
+
1044
+ Recover the original verifier model and explanation from retained modern and
1045
+ pre-header reports. The default command only reads files and makes authenticated
1046
+ GET requests. It writes a private manifest for review; `--apply` is a separate,
1047
+ explicit step. No reviewer is rerun, no triage events are invented, and retry
1048
+ queues and source reports remain untouched.
1049
+
1050
+ ```bash
1051
+ # Offline discovery, also usable before the compatible Harness backend is deployed.
1052
+ rcl telemetry recover-refutations --inventory-only --manifest inventory.json
1053
+
1054
+ # Plan against the authenticated organization. Repeated roots replace defaults.
1055
+ rcl telemetry recover-refutations --root ~/Development --root /tmp --manifest recovery.json
1056
+
1057
+ # Inspect coverage, source hashes, every refutation and each proposed action first.
1058
+ rcl telemetry recover-refutations --manifest recovery.json --apply --output outcome.json
1059
+ ```
1060
+
1061
+ The destination is the complete authenticated Harness base URL plus its
1062
+ server-reported organization. Applying with a different host, URL path or
1063
+ organization fails before any write. An older receiver without `meta.org_id`
1064
+ cannot produce an applyable manifest; use `--inventory-only` until the compatible
1065
+ backend is released. Inventory-only artifacts are never accepted for apply.
1066
+ Telemetry opt-outs and the existing Harness login/CI credential rules still apply.
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.
1129
+
1130
+ ```yaml
1131
+ # Blocking council (provider-prefixed names) — every round waits for these.
1132
+ # Shown here: the actual defaults. Keep slow/aggregator-routed models out of
1133
+ # this list; give them an async seat instead.
1134
+ models:
1135
+ - anthropic/claude-opus-5-5
1136
+ - openai/gpt-6-sol
1137
+
1138
+ # Specialist assignments only; no additional general reviewer.
1139
+ secondaryModels:
1140
+ - google/gemini-3.8-flash
1141
+
1142
+ # Async bonus reviewers — fired with each round, never awaited. Results that
1143
+ # have arrived by the next round of the same target are merged into that
1144
+ # 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
+ asyncModels:
1147
+ - openrouter/moonshotai/kimi-k3
1148
+
1149
+ # Default roles to run
1150
+ roles:
1151
+ - security-auditor
1152
+ - 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
+
1162
+ # Custom role overrides (extends a built-in or creates new)
1163
+ customRoles:
1164
+ - name: my-style-guide
1165
+ focus: [best-practices]
1166
+ systemPrompt: |
1167
+ Enforce our team style guide. Flag any deviation from snake_case
1168
+ variable names and require docstrings on all public functions.
1169
+
1170
+ # Consensus and deduplication thresholds
1171
+ thresholds:
1172
+ minConsensusScore: 0.4 # 0–1; findings below this are demoted to the appendix
1173
+ minConfidence: 0.2
1174
+ dedupeLineWindow: 5 # lines within which findings are merged
1175
+ jaccardThreshold: 0.3 # weighted title+description similarity threshold for dedup
1176
+
1177
+ # Convergence gating: which findings block convergence / CI (RCL-23).
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).
1184
+ gating:
1185
+ mode: verified-consensus # or all-findings (legacy: severity alone decides)
1186
+ minModels: 2 # distinct models for consensus gating
1187
+ verificationModel: openai/gpt-6-astra # direct-API only
1188
+ verificationReasoningEffort: high # OpenAI verifier only; separate from reviewer effort
1189
+ verificationPassTimeout: 600000 # ms for the complete verification queue
1190
+
1191
+ # Output defaults
1192
+ output:
1193
+ markdown: true
1194
+ markdownPath: review-report.md
1195
+ belowThresholdAppendix: true # false drops below-threshold findings outright
1196
+
1197
+ # Concurrency and reliability
1198
+ concurrency: 9 # maximum simultaneous blocking reviewer calls per process
1199
+ # set to 6 to retain the previous limit
1200
+ providerConcurrency: # provider admission caps, applied in addition to concurrency
1201
+ anthropic: 2 # default: bound high-effort Anthropic bursts
1202
+ timeout: 540000 # ms per blocking model call (matches the current default)
1203
+ asyncTimeout: 900000 # ms per async-lane call (slow reasoning models get headroom; nothing waits on them)
1204
+ # quorumFraction: 0.75 # round closes once this share of blocking seats succeeds
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.
1211
+ maxRetries: 3
1212
+
1213
+ # Reasoning budget for providers that support it (currently OpenRouter).
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.
1218
+ reasoningEffort: low
1219
+
1220
+ # Context files to attach to every review
1221
+ context:
1222
+ - ARCHITECTURE.md
1223
+ - docs/api.md
1224
+
1225
+ # Spec file for spec-compliance role
1226
+ spec: SPEC.md
1227
+
1228
+ # GitHub token (prefer GITHUB_TOKEN env var instead)
1229
+ # githubToken: ghp_...
1230
+ ```
1231
+
1232
+ `concurrency` limits all blocking reviewer calls within one RCL process.
1233
+ `providerConcurrency` adds stricter provider admission caps; increasing the global
1234
+ limit never bypasses them. The scheduler scans past a saturated provider so calls
1235
+ for other providers continue without changing the original result order. Anthropic
1236
+ defaults to two concurrent calls to prevent the observed five-seat high-effort burst;
1237
+ the existing 540-second call deadline is unchanged. Providers with no default or
1238
+ explicit entry use only the global limit. Separate RCL processes do not share these
1239
+ limits; async reviewers and verification use their own scheduling.
1240
+
1241
+ `maxRetries` limits additional adapter SDK invocations after the first attempt;
1242
+ all attempts share the call's `timeout` and parent cancellation signal. Supported
1243
+ transient connection failures and HTTP status errors may retry; cancellation,
1244
+ expired deadlines, permanent TLS/configuration errors and unusable output do not.
1245
+ Report reviews and `ask` results expose `adapterAttempts` when observed. Chunk
1246
+ reports sum it only when every part has a known count. It is neither a wire-request
1247
+ count nor a billing total: lower-level activity and charges after ambiguous
1248
+ transport failures can be unknown. Token usage remains what the SDK response
1249
+ exposes, not proof of total charges across retries.
1250
+
1251
+ Supported config file names: `.review-council.yml`, `.review-council.yaml`, `.review-council.json`. Executable JS config is never discovered: rcl often runs in untrusted checkouts with provider keys in the environment.
1252
+
1253
+ The verifier uses three verdicts: `confirmed`, `refuted`, and
1254
+ `insufficient_evidence`. Confirmation requires a reachable failure mechanism and
1255
+ exact code excerpts from the supplied change; citations are checked against that
1256
+ source before a claim can be promoted to `verified`. Missing context or inability
1257
+ to refute a claim is not confirmation. This checks citation provenance, not semantic
1258
+ truth: the verifier still has to reason correctly. Infrastructure failures remain
1259
+ `unavailable`. Historical `unrefuted` verdicts retain their original meaning.
1260
+
1261
+ Astra defaults to `high` effort. Set `gating.verificationReasoningEffort` to `low`,
1262
+ `medium`, `high`, `xhigh`, or `max` for an OpenAI verifier. The setting is included
1263
+ in the report header and retained verification plan. Other provider overrides
1264
+ retain their provider effort defaults and reject this OpenAI-only setting.
1265
+
1266
+ The top-level `reasoningEffort` applies only to OpenRouter reviewers. Direct
1267
+ Sol and Gemini reviewers use their provider defaults (currently `medium`).
1268
+ Opus 5.5 reviews explicitly use `high`, streaming, and a 65,536-token output
1269
+ ceiling so thinking and findings share adequate headroom. Opus 5.5's API default
1270
+ is `medium`; RCL sets `high` because a review gate is intelligence-sensitive work
1271
+ ([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. RCL's whole-call
1273
+ timeout and rejection of incomplete output are unchanged.
1274
+ OpenRouter reviewers default to `low`, a supported level for the default Kimi K3;
1275
+ explicit `reasoningEffort` overrides are preserved. Kimi K3 advertises `low`,
1276
+ `high`, and `max`, so avoid overriding it to `medium`. Effort labels are
1277
+ provider-specific and do not imply equal compute or quality across models.
1278
+
1279
+ The default whole-pass deadline is 10 minutes across all queued verifier
1280
+ batches. Verifier calls default to the remaining whole-pass budget. Set the optional
1281
+ `gating.verificationTimeout` in milliseconds to impose a shorter per-call limit;
1282
+ every call is still capped by the remaining `verificationPassTimeout` deadline.
1283
+
1284
+ For converging patch reviews, async collection uses `--converge-target` (or
1285
+ `RCL_CONVERGE_TARGET`), not the patch pathname. Each round can keep a distinct,
1286
+ immutable capture while sharing results across linked worktrees of the same
1287
+ repository and target. Other review modes retain their existing keys; previously
1288
+ spooled path-keyed results are not migrated. Async findings can come from an
1289
+ earlier capture and still need checking against the current code. This does not
1290
+ make detached-worker completion part of the blocking round. `run.roster` records
1291
+ this round's planned seats; collected async reviews retain their model and role
1292
+ in `reviews` and can come from seats absent from the current roster.
1293
+
1294
+ Before dispatch, RCL prints the expanded reviewer × chunk call count,
1295
+ concurrency, wave count, timeout, and timeout-bound queue estimate. Interactive
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.
1299
+
1300
+ ---
1301
+
1302
+ ## How Consensus Works
1303
+
1304
+ When multiple models and roles review the same diff, their findings are:
1305
+
1306
+ 1. **Deduplicated** — findings on the same file and overlapping line range are grouped by weighted title+description token similarity; findings in different categories can still merge, but need stronger similarity (models disagree on category boundaries constantly). Findings whose line ranges strictly overlap and that name the same issue concept (sql injection, IDOR, hardcoded secret, …) merge regardless of wording — models phrase the same issue too differently for token overlap alone. Repeats within a single review are collapsed first. Findings that clearly reach opposite conclusions are kept as separate, disputed findings; subtler contradictions merge but are flagged as disputed.
1307
+ 2. **Scored** — each group receives a consensus score based on three dimensions: reviewer diversity (how many distinct models and roles flagged it, saturating at half the fleet so large configurations aren't penalized), role relevance (whether a role specialised in that finding type confirmed it), and isolation (what fraction of relevant reviewers flagged it).
1308
+ 3. **Classified** — groups are assigned a confidence band (Very High → Minimal) and a final severity. Severity is the most common rating across reviewers; when reviewers disagree, high-confidence agreement elevates it, but only to a severity at least two reviewers independently assigned — a lone outlier rating is surfaced as a dispute instead. Each group also gets an **agreement tier** measured over distinct models — `unanimous` (every successful model), `majority` (at least half), `minority` (2+, under half), `single` (one model) — because roles share a model's blind spots, so model count is the evidence axis.
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.
1310
+
1311
+ The report is organized by agreement tier — unanimous first, then majority, minority, **disputed** (reviewers reached materially different conclusions; rendered as per-model positions so you can judge), and single-model last. Within each tier, findings sort by severity. The tier structure is the point of a multi-model council: it tells you which findings are independently confirmed and where to spend your own judgment.
1312
+
1313
+ For the full algorithm, see [CONSENSUS_V2_SPEC.md](./CONSENSUS_V2_SPEC.md).
1314
+
1315
+ ---
1316
+
1317
+ ## Environment Variables
1318
+
1319
+ Explicit GitHub PR fetches and review posting use a nonempty `githubToken`
1320
+ configuration value first, then `GITHUB_TOKEN`, then the existing
1321
+ `gh auth token --hostname github.com` login. The fallback is noninteractive
1322
+ and bounded; if unavailable, public anonymous reads still work. A PR 404
1323
+ explains how to check private-repository access without exposing credentials.
1324
+ Local patch reviews do not read GitHub credentials.
1325
+
1326
+ | Variable | Description |
1327
+ |----------|-------------|
1328
+ | `ANTHROPIC_API_KEY` | API key for Claude models |
1329
+ | `OPENAI_API_KEY` | API key for OpenAI models |
1330
+ | `GOOGLE_API_KEY` | Preferred Google Gemini API key; empty or whitespace-only values fall through |
1331
+ | `GEMINI_API_KEY` | Google Gemini API key when `GOOGLE_API_KEY` is absent or blank; also used for Harness-injected keys |
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.
1369
+
1370
+ ---
1371
+
1372
+ ## License
1373
+
1374
+ MIT © 2026 Michael Ströck
1375
+
1376
+ ### Recovering a terminal local evidence rejection
1377
+
1378
+ A locally invalid report is retained in quarantine, outside the retryable outbox.
1379
+ `deliveryFailure: local-invalid` describes delivery, independently of reviewer
1380
+ quorum. It does not admit the report or authorize another review. Transport
1381
+ failures and spooled evidence retain their existing delivery gates.
1382
+
1383
+ For an ordinary completed report rejected for missing verified-consensus gating
1384
+ labels, preview a disposition using its original report and exact digest:
1385
+
1386
+ ```bash
1387
+ rcl converge-rejected --preview --target owner-repo-123 --run ORIGINAL_RUN_UUID \
1388
+ --report /path/to/original.json --report-sha256 ORIGINAL_SHA256 \
1389
+ --reason "Original local rejection diagnosed; producer repair verified" \
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
+ ```
1394
+
1395
+ The command verifies the original report, quarantine diagnostics and envelope,
1396
+ blocking health, run/head/input identity, cycle and latest spent attempt. It
1397
+ requires an exited coordinator and no admitted round or queued delivery. Valid
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.