@quickchatai/pi-quickreview 0.0.0-stage → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (697) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +75 -2
  3. package/assets/agents/pass-abstraction.md +64 -0
  4. package/assets/agents/pass-ask-human.md +134 -0
  5. package/assets/agents/pass-backcompat.md +67 -0
  6. package/assets/agents/pass-baseline.md +61 -0
  7. package/assets/agents/pass-behaviour-delta.md +64 -0
  8. package/assets/agents/pass-bounds.md +36 -0
  9. package/assets/agents/pass-claims.md +31 -0
  10. package/assets/agents/pass-concurrency.md +168 -0
  11. package/assets/agents/pass-consistency.md +57 -0
  12. package/assets/agents/pass-contracts.md +30 -0
  13. package/assets/agents/pass-data-safety.md +54 -0
  14. package/assets/agents/pass-deferred.md +35 -0
  15. package/assets/agents/pass-delivery.md +37 -0
  16. package/assets/agents/pass-duplication.md +60 -0
  17. package/assets/agents/pass-intent.md +63 -0
  18. package/assets/agents/pass-lint-conformance.md +46 -0
  19. package/assets/agents/pass-maintainability.md +67 -0
  20. package/assets/agents/pass-placement.md +68 -0
  21. package/assets/agents/pass-prod-risk.md +24 -0
  22. package/assets/agents/pass-rollout.md +32 -0
  23. package/assets/agents/pass-roundtrip.md +46 -0
  24. package/assets/agents/pass-security.md +31 -0
  25. package/assets/agents/pass-simplicity.md +68 -0
  26. package/assets/agents/pass-statelessness.md +27 -0
  27. package/assets/agents/pass-style.md +37 -0
  28. package/assets/agents/pass-sweep.md +57 -0
  29. package/assets/agents/pass-tests.md +58 -0
  30. package/assets/agents/pass-ui.md +23 -0
  31. package/assets/agents/pass-validate.md +50 -0
  32. package/assets/agents/pass-widened.md +46 -0
  33. package/assets/agents/scan/pass-scan.md +86 -0
  34. package/assets/config.example.toml +1099 -0
  35. package/assets/deploy/reviewer/AGENTS.override.md +12 -0
  36. package/assets/profiles/default/README.md +21 -0
  37. package/assets/profiles/default/addenda/.gitkeep +0 -0
  38. package/assets/profiles/default/profile.toml +17 -0
  39. package/assets/profiles/example-django/README.md +21 -0
  40. package/assets/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  41. package/assets/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  42. package/assets/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  43. package/assets/profiles/example-django/addenda/40-orm-usage.md +19 -0
  44. package/assets/profiles/example-django/addenda/50-declared-shape.md +10 -0
  45. package/assets/profiles/example-django/addenda/60-config-placement.md +14 -0
  46. package/assets/profiles/example-django/heuristics.toml +24 -0
  47. package/assets/profiles/example-django/profile.toml +9 -0
  48. package/assets/profiles/fast/README.md +24 -0
  49. package/assets/profiles/fast/profile.toml +59 -0
  50. package/assets/profiles/minimal/README.md +17 -0
  51. package/assets/profiles/minimal/addenda/.gitkeep +0 -0
  52. package/assets/profiles/minimal/profile.toml +8 -0
  53. package/assets/profiles/snappy/README.md +24 -0
  54. package/assets/profiles/snappy/addenda/10-race-as-question.md +6 -0
  55. package/assets/profiles/snappy/profile.toml +46 -0
  56. package/assets/prompts/companion-day-open.md +46 -0
  57. package/assets/prompts/companion-first-run.md +104 -0
  58. package/assets/prompts/companion.md +821 -0
  59. package/assets/prompts/conversation-system.md +117 -0
  60. package/assets/prompts/eod-sweep.md +120 -0
  61. package/assets/prompts/onboarding.md +113 -0
  62. package/assets/prompts/pass-common.md +52 -0
  63. package/assets/prompts/pass-quality-common.md +22 -0
  64. package/assets/prompts/round-first.md +874 -0
  65. package/assets/prompts/round-follow-up.md +991 -0
  66. package/assets/prompts/round-re-review.md +889 -0
  67. package/assets/prompts/round-recheck.md +196 -0
  68. package/assets/prompts/round-staleness.md +856 -0
  69. package/assets/schemas/review-result.v1.json +391 -0
  70. package/assets/schemas/scan-result.v1.json +421 -0
  71. package/assets/schemas/version.v1.json +57 -0
  72. package/assets/seed-manifest.json +19 -0
  73. package/assets/seeds/agents/pass-baseline.md +61 -0
  74. package/assets/seeds/agents/pass-contracts.md +30 -0
  75. package/assets/seeds/agents/pass-data-safety.md +54 -0
  76. package/assets/seeds/agents/pass-delivery.md +37 -0
  77. package/assets/seeds/agents/pass-prod-risk.md +24 -0
  78. package/assets/seeds/agents/pass-security.md +31 -0
  79. package/assets/seeds/agents/pass-statelessness.md +27 -0
  80. package/assets/seeds/agents/pass-ui.md +23 -0
  81. package/assets/seeds/agents/pass-validate.md +50 -0
  82. package/assets/seeds/prompts/conversation-system.md +117 -0
  83. package/assets/seeds/prompts/eod-sweep.md +120 -0
  84. package/assets/seeds/prompts/pass-common.md +52 -0
  85. package/dist/extensions/bash-guard.d.ts +467 -0
  86. package/dist/extensions/bash-guard.d.ts.map +1 -0
  87. package/dist/extensions/bash-guard.js +3058 -0
  88. package/dist/extensions/bash-guard.js.map +1 -0
  89. package/dist/extensions/finding-clusters.d.ts +33 -0
  90. package/dist/extensions/finding-clusters.d.ts.map +1 -0
  91. package/dist/extensions/finding-clusters.js +218 -0
  92. package/dist/extensions/finding-clusters.js.map +1 -0
  93. package/dist/extensions/ledger-pin.d.ts +76 -0
  94. package/dist/extensions/ledger-pin.d.ts.map +1 -0
  95. package/dist/extensions/ledger-pin.js +331 -0
  96. package/dist/extensions/ledger-pin.js.map +1 -0
  97. package/dist/extensions/lens.d.ts +43 -0
  98. package/dist/extensions/lens.d.ts.map +1 -0
  99. package/dist/extensions/lens.js +150 -0
  100. package/dist/extensions/lens.js.map +1 -0
  101. package/dist/extensions/pi-tool.d.ts +22 -0
  102. package/dist/extensions/pi-tool.d.ts.map +1 -0
  103. package/dist/extensions/pi-tool.js +20 -0
  104. package/dist/extensions/pi-tool.js.map +1 -0
  105. package/dist/extensions/quickreview.d.ts +21 -0
  106. package/dist/extensions/quickreview.d.ts.map +1 -0
  107. package/dist/extensions/quickreview.js +132 -0
  108. package/dist/extensions/quickreview.js.map +1 -0
  109. package/dist/src/anchors.d.ts +54 -0
  110. package/dist/src/anchors.d.ts.map +1 -0
  111. package/dist/src/anchors.js +127 -0
  112. package/dist/src/anchors.js.map +1 -0
  113. package/dist/src/assetroot.d.ts +16 -0
  114. package/dist/src/assetroot.d.ts.map +1 -0
  115. package/dist/src/assetroot.js +77 -0
  116. package/dist/src/assetroot.js.map +1 -0
  117. package/dist/src/authwatch.d.ts +61 -0
  118. package/dist/src/authwatch.d.ts.map +1 -0
  119. package/dist/src/authwatch.js +210 -0
  120. package/dist/src/authwatch.js.map +1 -0
  121. package/dist/src/budget.d.ts +15 -0
  122. package/dist/src/budget.d.ts.map +1 -0
  123. package/dist/src/budget.js +32 -0
  124. package/dist/src/budget.js.map +1 -0
  125. package/dist/src/casebudget.d.ts +3 -0
  126. package/dist/src/casebudget.d.ts.map +1 -0
  127. package/dist/src/casebudget.js +3 -0
  128. package/dist/src/casebudget.js.map +1 -0
  129. package/dist/src/cigate.d.ts +116 -0
  130. package/dist/src/cigate.d.ts.map +1 -0
  131. package/dist/src/cigate.js +176 -0
  132. package/dist/src/cigate.js.map +1 -0
  133. package/dist/src/codespan.d.ts +46 -0
  134. package/dist/src/codespan.d.ts.map +1 -0
  135. package/dist/src/codespan.js +75 -0
  136. package/dist/src/codespan.js.map +1 -0
  137. package/dist/src/contextpack.d.ts +56 -0
  138. package/dist/src/contextpack.d.ts.map +1 -0
  139. package/dist/src/contextpack.js +195 -0
  140. package/dist/src/contextpack.js.map +1 -0
  141. package/dist/src/contract.d.ts +8 -0
  142. package/dist/src/contract.d.ts.map +1 -0
  143. package/dist/src/contract.js +21 -0
  144. package/dist/src/contract.js.map +1 -0
  145. package/dist/src/coverage.d.ts +32 -0
  146. package/dist/src/coverage.d.ts.map +1 -0
  147. package/dist/src/coverage.js +85 -0
  148. package/dist/src/coverage.js.map +1 -0
  149. package/dist/src/datafence.d.ts +19 -0
  150. package/dist/src/datafence.d.ts.map +1 -0
  151. package/dist/src/datafence.js +60 -0
  152. package/dist/src/datafence.js.map +1 -0
  153. package/dist/src/dedup.d.ts +65 -0
  154. package/dist/src/dedup.d.ts.map +1 -0
  155. package/dist/src/dedup.js +459 -0
  156. package/dist/src/dedup.js.map +1 -0
  157. package/dist/src/depsources.d.ts +304 -0
  158. package/dist/src/depsources.d.ts.map +1 -0
  159. package/dist/src/depsources.js +3367 -0
  160. package/dist/src/depsources.js.map +1 -0
  161. package/dist/src/diffinput.d.ts +10 -0
  162. package/dist/src/diffinput.d.ts.map +1 -0
  163. package/dist/src/diffinput.js +172 -0
  164. package/dist/src/diffinput.js.map +1 -0
  165. package/dist/src/doubtwords.d.ts +10 -0
  166. package/dist/src/doubtwords.d.ts.map +1 -0
  167. package/dist/src/doubtwords.js +13 -0
  168. package/dist/src/doubtwords.js.map +1 -0
  169. package/dist/src/ecosystem.d.ts +148 -0
  170. package/dist/src/ecosystem.d.ts.map +1 -0
  171. package/dist/src/ecosystem.js +338 -0
  172. package/dist/src/ecosystem.js.map +1 -0
  173. package/dist/src/engine/agentdir.d.ts +5 -0
  174. package/dist/src/engine/agentdir.d.ts.map +1 -0
  175. package/dist/src/engine/agentdir.js +21 -0
  176. package/dist/src/engine/agentdir.js.map +1 -0
  177. package/dist/src/engine/auth.d.ts +214 -0
  178. package/dist/src/engine/auth.d.ts.map +1 -0
  179. package/dist/src/engine/auth.js +374 -0
  180. package/dist/src/engine/auth.js.map +1 -0
  181. package/dist/src/engine/budget.d.ts +60 -0
  182. package/dist/src/engine/budget.d.ts.map +1 -0
  183. package/dist/src/engine/budget.js +113 -0
  184. package/dist/src/engine/budget.js.map +1 -0
  185. package/dist/src/engine/capfd.d.ts +6 -0
  186. package/dist/src/engine/capfd.d.ts.map +1 -0
  187. package/dist/src/engine/capfd.js +21 -0
  188. package/dist/src/engine/capfd.js.map +1 -0
  189. package/dist/src/engine/caseout.d.ts +66 -0
  190. package/dist/src/engine/caseout.d.ts.map +1 -0
  191. package/dist/src/engine/caseout.js +107 -0
  192. package/dist/src/engine/caseout.js.map +1 -0
  193. package/dist/src/engine/context.d.ts +57 -0
  194. package/dist/src/engine/context.d.ts.map +1 -0
  195. package/dist/src/engine/context.js +212 -0
  196. package/dist/src/engine/context.js.map +1 -0
  197. package/dist/src/engine/envmap.d.ts +13 -0
  198. package/dist/src/engine/envmap.d.ts.map +1 -0
  199. package/dist/src/engine/envmap.js +377 -0
  200. package/dist/src/engine/envmap.js.map +1 -0
  201. package/dist/src/engine/extfiles.d.ts +7 -0
  202. package/dist/src/engine/extfiles.d.ts.map +1 -0
  203. package/dist/src/engine/extfiles.js +23 -0
  204. package/dist/src/engine/extfiles.js.map +1 -0
  205. package/dist/src/engine/keyroute.d.ts +21 -0
  206. package/dist/src/engine/keyroute.d.ts.map +1 -0
  207. package/dist/src/engine/keyroute.js +58 -0
  208. package/dist/src/engine/keyroute.js.map +1 -0
  209. package/dist/src/engine/lens/cache-group.d.ts +20 -0
  210. package/dist/src/engine/lens/cache-group.d.ts.map +1 -0
  211. package/dist/src/engine/lens/cache-group.js +31 -0
  212. package/dist/src/engine/lens/cache-group.js.map +1 -0
  213. package/dist/src/engine/lens/cache-run.d.ts +65 -0
  214. package/dist/src/engine/lens/cache-run.d.ts.map +1 -0
  215. package/dist/src/engine/lens/cache-run.js +159 -0
  216. package/dist/src/engine/lens/cache-run.js.map +1 -0
  217. package/dist/src/engine/lens/deadline.d.ts +54 -0
  218. package/dist/src/engine/lens/deadline.d.ts.map +1 -0
  219. package/dist/src/engine/lens/deadline.js +111 -0
  220. package/dist/src/engine/lens/deadline.js.map +1 -0
  221. package/dist/src/engine/lens/engine-tools.d.ts +13 -0
  222. package/dist/src/engine/lens/engine-tools.d.ts.map +1 -0
  223. package/dist/src/engine/lens/engine-tools.js +37 -0
  224. package/dist/src/engine/lens/engine-tools.js.map +1 -0
  225. package/dist/src/engine/lens/env.d.ts +23 -0
  226. package/dist/src/engine/lens/env.d.ts.map +1 -0
  227. package/dist/src/engine/lens/env.js +42 -0
  228. package/dist/src/engine/lens/env.js.map +1 -0
  229. package/dist/src/engine/lens/launch-plan.d.ts +63 -0
  230. package/dist/src/engine/lens/launch-plan.d.ts.map +1 -0
  231. package/dist/src/engine/lens/launch-plan.js +203 -0
  232. package/dist/src/engine/lens/launch-plan.js.map +1 -0
  233. package/dist/src/engine/lens/leaf-cache.d.ts +22 -0
  234. package/dist/src/engine/lens/leaf-cache.d.ts.map +1 -0
  235. package/dist/src/engine/lens/leaf-cache.js +115 -0
  236. package/dist/src/engine/lens/leaf-cache.js.map +1 -0
  237. package/dist/src/engine/lens/permits.d.ts +14 -0
  238. package/dist/src/engine/lens/permits.d.ts.map +1 -0
  239. package/dist/src/engine/lens/permits.js +18 -0
  240. package/dist/src/engine/lens/permits.js.map +1 -0
  241. package/dist/src/engine/lens/registry.d.ts +55 -0
  242. package/dist/src/engine/lens/registry.d.ts.map +1 -0
  243. package/dist/src/engine/lens/registry.js +140 -0
  244. package/dist/src/engine/lens/registry.js.map +1 -0
  245. package/dist/src/engine/lens/round-lens.d.ts +16 -0
  246. package/dist/src/engine/lens/round-lens.d.ts.map +1 -0
  247. package/dist/src/engine/lens/round-lens.js +46 -0
  248. package/dist/src/engine/lens/round-lens.js.map +1 -0
  249. package/dist/src/engine/lens/runlens.d.ts +103 -0
  250. package/dist/src/engine/lens/runlens.d.ts.map +1 -0
  251. package/dist/src/engine/lens/runlens.js +538 -0
  252. package/dist/src/engine/lens/runlens.js.map +1 -0
  253. package/dist/src/engine/lens/section.d.ts +19 -0
  254. package/dist/src/engine/lens/section.d.ts.map +1 -0
  255. package/dist/src/engine/lens/section.js +40 -0
  256. package/dist/src/engine/lens/section.js.map +1 -0
  257. package/dist/src/engine/lens/semaphore.d.ts +13 -0
  258. package/dist/src/engine/lens/semaphore.d.ts.map +1 -0
  259. package/dist/src/engine/lens/semaphore.js +44 -0
  260. package/dist/src/engine/lens/semaphore.js.map +1 -0
  261. package/dist/src/engine/lens/tool.d.ts +34 -0
  262. package/dist/src/engine/lens/tool.d.ts.map +1 -0
  263. package/dist/src/engine/lens/tool.js +345 -0
  264. package/dist/src/engine/lens/tool.js.map +1 -0
  265. package/dist/src/engine/lens/wave.d.ts +63 -0
  266. package/dist/src/engine/lens/wave.d.ts.map +1 -0
  267. package/dist/src/engine/lens/wave.js +95 -0
  268. package/dist/src/engine/lens/wave.js.map +1 -0
  269. package/dist/src/engine/livesessions.d.ts +21 -0
  270. package/dist/src/engine/livesessions.d.ts.map +1 -0
  271. package/dist/src/engine/livesessions.js +57 -0
  272. package/dist/src/engine/livesessions.js.map +1 -0
  273. package/dist/src/engine/livewire.d.ts +18 -0
  274. package/dist/src/engine/livewire.d.ts.map +1 -0
  275. package/dist/src/engine/livewire.js +31 -0
  276. package/dist/src/engine/livewire.js.map +1 -0
  277. package/dist/src/engine/openable.d.ts +37 -0
  278. package/dist/src/engine/openable.d.ts.map +1 -0
  279. package/dist/src/engine/openable.js +120 -0
  280. package/dist/src/engine/openable.js.map +1 -0
  281. package/dist/src/engine/pisdk.d.ts +235 -0
  282. package/dist/src/engine/pisdk.d.ts.map +1 -0
  283. package/dist/src/engine/pisdk.js +577 -0
  284. package/dist/src/engine/pisdk.js.map +1 -0
  285. package/dist/src/engine/port.d.ts +144 -0
  286. package/dist/src/engine/port.d.ts.map +1 -0
  287. package/dist/src/engine/port.js +16 -0
  288. package/dist/src/engine/port.js.map +1 -0
  289. package/dist/src/engine/prompts.d.ts +159 -0
  290. package/dist/src/engine/prompts.d.ts.map +1 -0
  291. package/dist/src/engine/prompts.js +339 -0
  292. package/dist/src/engine/prompts.js.map +1 -0
  293. package/dist/src/engine/render.d.ts +10 -0
  294. package/dist/src/engine/render.d.ts.map +1 -0
  295. package/dist/src/engine/render.js +23 -0
  296. package/dist/src/engine/render.js.map +1 -0
  297. package/dist/src/engine/reviewruntime.d.ts +13 -0
  298. package/dist/src/engine/reviewruntime.d.ts.map +1 -0
  299. package/dist/src/engine/reviewruntime.js +35 -0
  300. package/dist/src/engine/reviewruntime.js.map +1 -0
  301. package/dist/src/engine/reviewsdk.d.ts +75 -0
  302. package/dist/src/engine/reviewsdk.d.ts.map +1 -0
  303. package/dist/src/engine/reviewsdk.js +180 -0
  304. package/dist/src/engine/reviewsdk.js.map +1 -0
  305. package/dist/src/engine/round.d.ts +166 -0
  306. package/dist/src/engine/round.d.ts.map +1 -0
  307. package/dist/src/engine/round.js +543 -0
  308. package/dist/src/engine/round.js.map +1 -0
  309. package/dist/src/engine/roundagents.d.ts +17 -0
  310. package/dist/src/engine/roundagents.d.ts.map +1 -0
  311. package/dist/src/engine/roundagents.js +49 -0
  312. package/dist/src/engine/roundagents.js.map +1 -0
  313. package/dist/src/engine/roundevents.d.ts +110 -0
  314. package/dist/src/engine/roundevents.d.ts.map +1 -0
  315. package/dist/src/engine/roundevents.js +276 -0
  316. package/dist/src/engine/roundevents.js.map +1 -0
  317. package/dist/src/engine/roundsdk.d.ts +33 -0
  318. package/dist/src/engine/roundsdk.d.ts.map +1 -0
  319. package/dist/src/engine/roundsdk.js +76 -0
  320. package/dist/src/engine/roundsdk.js.map +1 -0
  321. package/dist/src/engine/sdkruntime.d.ts +23 -0
  322. package/dist/src/engine/sdkruntime.d.ts.map +1 -0
  323. package/dist/src/engine/sdkruntime.js +340 -0
  324. package/dist/src/engine/sdkruntime.js.map +1 -0
  325. package/dist/src/engine/select.d.ts +16 -0
  326. package/dist/src/engine/select.d.ts.map +1 -0
  327. package/dist/src/engine/select.js +32 -0
  328. package/dist/src/engine/select.js.map +1 -0
  329. package/dist/src/engine/sessionwrap.d.ts +4 -0
  330. package/dist/src/engine/sessionwrap.d.ts.map +1 -0
  331. package/dist/src/engine/sessionwrap.js +31 -0
  332. package/dist/src/engine/sessionwrap.js.map +1 -0
  333. package/dist/src/engine/toolfence.d.ts +90 -0
  334. package/dist/src/engine/toolfence.d.ts.map +1 -0
  335. package/dist/src/engine/toolfence.js +378 -0
  336. package/dist/src/engine/toolfence.js.map +1 -0
  337. package/dist/src/engine/tooluid.d.ts +128 -0
  338. package/dist/src/engine/tooluid.d.ts.map +1 -0
  339. package/dist/src/engine/tooluid.js +401 -0
  340. package/dist/src/engine/tooluid.js.map +1 -0
  341. package/dist/src/envstrip.d.ts +10 -0
  342. package/dist/src/envstrip.d.ts.map +1 -0
  343. package/dist/src/envstrip.js +15 -0
  344. package/dist/src/envstrip.js.map +1 -0
  345. package/dist/src/escalationprompt.d.ts +22 -0
  346. package/dist/src/escalationprompt.d.ts.map +1 -0
  347. package/dist/src/escalationprompt.js +54 -0
  348. package/dist/src/escalationprompt.js.map +1 -0
  349. package/dist/src/execfn.d.ts +29 -0
  350. package/dist/src/execfn.d.ts.map +1 -0
  351. package/dist/src/execfn.js +55 -0
  352. package/dist/src/execfn.js.map +1 -0
  353. package/dist/src/finding-chain.d.ts +40 -0
  354. package/dist/src/finding-chain.d.ts.map +1 -0
  355. package/dist/src/finding-chain.js +76 -0
  356. package/dist/src/finding-chain.js.map +1 -0
  357. package/dist/src/finding-row.d.ts +24 -0
  358. package/dist/src/finding-row.d.ts.map +1 -0
  359. package/dist/src/finding-row.js +26 -0
  360. package/dist/src/finding-row.js.map +1 -0
  361. package/dist/src/findingkey.d.ts +99 -0
  362. package/dist/src/findingkey.d.ts.map +1 -0
  363. package/dist/src/findingkey.js +210 -0
  364. package/dist/src/findingkey.js.map +1 -0
  365. package/dist/src/findingsrecord.d.ts +175 -0
  366. package/dist/src/findingsrecord.d.ts.map +1 -0
  367. package/dist/src/findingsrecord.js +410 -0
  368. package/dist/src/findingsrecord.js.map +1 -0
  369. package/dist/src/followup-mode.d.ts +4 -0
  370. package/dist/src/followup-mode.d.ts.map +1 -0
  371. package/dist/src/followup-mode.js +6 -0
  372. package/dist/src/followup-mode.js.map +1 -0
  373. package/dist/src/gate.d.ts +199 -0
  374. package/dist/src/gate.d.ts.map +1 -0
  375. package/dist/src/gate.js +315 -0
  376. package/dist/src/gate.js.map +1 -0
  377. package/dist/src/ghcred.d.ts +58 -0
  378. package/dist/src/ghcred.d.ts.map +1 -0
  379. package/dist/src/ghcred.js +139 -0
  380. package/dist/src/ghcred.js.map +1 -0
  381. package/dist/src/githubAuth.d.ts +70 -0
  382. package/dist/src/githubAuth.d.ts.map +1 -0
  383. package/dist/src/githubAuth.js +183 -0
  384. package/dist/src/githubAuth.js.map +1 -0
  385. package/dist/src/githubcore.d.ts +20 -0
  386. package/dist/src/githubcore.d.ts.map +1 -0
  387. package/dist/src/githubcore.js +20 -0
  388. package/dist/src/githubcore.js.map +1 -0
  389. package/dist/src/glob.d.ts +11 -0
  390. package/dist/src/glob.d.ts.map +1 -0
  391. package/dist/src/glob.js +69 -0
  392. package/dist/src/glob.js.map +1 -0
  393. package/dist/src/guards.d.ts +106 -0
  394. package/dist/src/guards.d.ts.map +1 -0
  395. package/dist/src/guards.js +365 -0
  396. package/dist/src/guards.js.map +1 -0
  397. package/dist/src/heartbeat.d.ts +29 -0
  398. package/dist/src/heartbeat.d.ts.map +1 -0
  399. package/dist/src/heartbeat.js +87 -0
  400. package/dist/src/heartbeat.js.map +1 -0
  401. package/dist/src/heuristics.d.ts +247 -0
  402. package/dist/src/heuristics.d.ts.map +1 -0
  403. package/dist/src/heuristics.js +927 -0
  404. package/dist/src/heuristics.js.map +1 -0
  405. package/dist/src/homes.d.ts +181 -0
  406. package/dist/src/homes.d.ts.map +1 -0
  407. package/dist/src/homes.js +242 -0
  408. package/dist/src/homes.js.map +1 -0
  409. package/dist/src/hunkheader.d.ts +11 -0
  410. package/dist/src/hunkheader.d.ts.map +1 -0
  411. package/dist/src/hunkheader.js +18 -0
  412. package/dist/src/hunkheader.js.map +1 -0
  413. package/dist/src/identity.d.ts +34 -0
  414. package/dist/src/identity.d.ts.map +1 -0
  415. package/dist/src/identity.js +67 -0
  416. package/dist/src/identity.js.map +1 -0
  417. package/dist/src/include.d.ts +435 -0
  418. package/dist/src/include.d.ts.map +1 -0
  419. package/dist/src/include.js +1692 -0
  420. package/dist/src/include.js.map +1 -0
  421. package/dist/src/launchvocab.d.ts +36 -0
  422. package/dist/src/launchvocab.d.ts.map +1 -0
  423. package/dist/src/launchvocab.js +59 -0
  424. package/dist/src/launchvocab.js.map +1 -0
  425. package/dist/src/ledgercarry.d.ts +24 -0
  426. package/dist/src/ledgercarry.d.ts.map +1 -0
  427. package/dist/src/ledgercarry.js +109 -0
  428. package/dist/src/ledgercarry.js.map +1 -0
  429. package/dist/src/lensids.d.ts +7 -0
  430. package/dist/src/lensids.d.ts.map +1 -0
  431. package/dist/src/lensids.js +53 -0
  432. package/dist/src/lensids.js.map +1 -0
  433. package/dist/src/lensplanning.d.ts +8 -0
  434. package/dist/src/lensplanning.d.ts.map +1 -0
  435. package/dist/src/lensplanning.js +126 -0
  436. package/dist/src/lensplanning.js.map +1 -0
  437. package/dist/src/lensresult.d.ts +94 -0
  438. package/dist/src/lensresult.d.ts.map +1 -0
  439. package/dist/src/lensresult.js +162 -0
  440. package/dist/src/lensresult.js.map +1 -0
  441. package/dist/src/lenstuning.d.ts +97 -0
  442. package/dist/src/lenstuning.d.ts.map +1 -0
  443. package/dist/src/lenstuning.js +326 -0
  444. package/dist/src/lenstuning.js.map +1 -0
  445. package/dist/src/lintconfig.d.ts +31 -0
  446. package/dist/src/lintconfig.d.ts.map +1 -0
  447. package/dist/src/lintconfig.js +543 -0
  448. package/dist/src/lintconfig.js.map +1 -0
  449. package/dist/src/live/events.d.ts +188 -0
  450. package/dist/src/live/events.d.ts.map +1 -0
  451. package/dist/src/live/events.js +136 -0
  452. package/dist/src/live/events.js.map +1 -0
  453. package/dist/src/live/writer.d.ts +54 -0
  454. package/dist/src/live/writer.d.ts.map +1 -0
  455. package/dist/src/live/writer.js +516 -0
  456. package/dist/src/live/writer.js.map +1 -0
  457. package/dist/src/localreview.d.ts +128 -0
  458. package/dist/src/localreview.d.ts.map +1 -0
  459. package/dist/src/localreview.js +426 -0
  460. package/dist/src/localreview.js.map +1 -0
  461. package/dist/src/loosenedtests.d.ts +13 -0
  462. package/dist/src/loosenedtests.d.ts.map +1 -0
  463. package/dist/src/loosenedtests.js +188 -0
  464. package/dist/src/loosenedtests.js.map +1 -0
  465. package/dist/src/memoryport.d.ts +264 -0
  466. package/dist/src/memoryport.d.ts.map +1 -0
  467. package/dist/src/memoryport.js +445 -0
  468. package/dist/src/memoryport.js.map +1 -0
  469. package/dist/src/migrations.d.ts +106 -0
  470. package/dist/src/migrations.d.ts.map +1 -0
  471. package/dist/src/migrations.js +373 -0
  472. package/dist/src/migrations.js.map +1 -0
  473. package/dist/src/modelroute.d.ts +38 -0
  474. package/dist/src/modelroute.d.ts.map +1 -0
  475. package/dist/src/modelroute.js +105 -0
  476. package/dist/src/modelroute.js.map +1 -0
  477. package/dist/src/oneshot.d.ts +12 -0
  478. package/dist/src/oneshot.d.ts.map +1 -0
  479. package/dist/src/oneshot.js +8 -0
  480. package/dist/src/oneshot.js.map +1 -0
  481. package/dist/src/packing.d.ts +11 -0
  482. package/dist/src/packing.d.ts.map +1 -0
  483. package/dist/src/packing.js +31 -0
  484. package/dist/src/packing.js.map +1 -0
  485. package/dist/src/passes.d.ts +16 -0
  486. package/dist/src/passes.d.ts.map +1 -0
  487. package/dist/src/passes.js +104 -0
  488. package/dist/src/passes.js.map +1 -0
  489. package/dist/src/passtasks.d.ts +68 -0
  490. package/dist/src/passtasks.d.ts.map +1 -0
  491. package/dist/src/passtasks.js +360 -0
  492. package/dist/src/passtasks.js.map +1 -0
  493. package/dist/src/pathinside.d.ts +5 -0
  494. package/dist/src/pathinside.d.ts.map +1 -0
  495. package/dist/src/pathinside.js +9 -0
  496. package/dist/src/pathinside.js.map +1 -0
  497. package/dist/src/pibuiltins.d.ts +36 -0
  498. package/dist/src/pibuiltins.d.ts.map +1 -0
  499. package/dist/src/pibuiltins.js +83 -0
  500. package/dist/src/pibuiltins.js.map +1 -0
  501. package/dist/src/plankey.d.ts +4 -0
  502. package/dist/src/plankey.d.ts.map +1 -0
  503. package/dist/src/plankey.js +10 -0
  504. package/dist/src/plankey.js.map +1 -0
  505. package/dist/src/planner.d.ts +375 -0
  506. package/dist/src/planner.d.ts.map +1 -0
  507. package/dist/src/planner.js +722 -0
  508. package/dist/src/planner.js.map +1 -0
  509. package/dist/src/plugin/args.d.ts +19 -0
  510. package/dist/src/plugin/args.d.ts.map +1 -0
  511. package/dist/src/plugin/args.js +107 -0
  512. package/dist/src/plugin/args.js.map +1 -0
  513. package/dist/src/plugin/index.d.ts +3 -0
  514. package/dist/src/plugin/index.d.ts.map +1 -0
  515. package/dist/src/plugin/index.js +14 -0
  516. package/dist/src/plugin/index.js.map +1 -0
  517. package/dist/src/plugin/login.d.ts +42 -0
  518. package/dist/src/plugin/login.d.ts.map +1 -0
  519. package/dist/src/plugin/login.js +82 -0
  520. package/dist/src/plugin/login.js.map +1 -0
  521. package/dist/src/plugin/review.d.ts +27 -0
  522. package/dist/src/plugin/review.d.ts.map +1 -0
  523. package/dist/src/plugin/review.js +302 -0
  524. package/dist/src/plugin/review.js.map +1 -0
  525. package/dist/src/plugin/runner.d.ts +6 -0
  526. package/dist/src/plugin/runner.d.ts.map +1 -0
  527. package/dist/src/plugin/runner.js +65 -0
  528. package/dist/src/plugin/runner.js.map +1 -0
  529. package/dist/src/plugin/scan.d.ts +79 -0
  530. package/dist/src/plugin/scan.d.ts.map +1 -0
  531. package/dist/src/plugin/scan.js +348 -0
  532. package/dist/src/plugin/scan.js.map +1 -0
  533. package/dist/src/plugin/session.d.ts +56 -0
  534. package/dist/src/plugin/session.d.ts.map +1 -0
  535. package/dist/src/plugin/session.js +2 -0
  536. package/dist/src/plugin/session.js.map +1 -0
  537. package/dist/src/plugin/setup.d.ts +19 -0
  538. package/dist/src/plugin/setup.d.ts.map +1 -0
  539. package/dist/src/plugin/setup.js +142 -0
  540. package/dist/src/plugin/setup.js.map +1 -0
  541. package/dist/src/plugin/versiongate.d.ts +26 -0
  542. package/dist/src/plugin/versiongate.d.ts.map +1 -0
  543. package/dist/src/plugin/versiongate.js +83 -0
  544. package/dist/src/plugin/versiongate.js.map +1 -0
  545. package/dist/src/postingkey.d.ts +8 -0
  546. package/dist/src/postingkey.d.ts.map +1 -0
  547. package/dist/src/postingkey.js +25 -0
  548. package/dist/src/postingkey.js.map +1 -0
  549. package/dist/src/postingpolicy.d.ts +108 -0
  550. package/dist/src/postingpolicy.d.ts.map +1 -0
  551. package/dist/src/postingpolicy.js +341 -0
  552. package/dist/src/postingpolicy.js.map +1 -0
  553. package/dist/src/pr-identity.d.ts +3 -0
  554. package/dist/src/pr-identity.d.ts.map +1 -0
  555. package/dist/src/pr-identity.js +9 -0
  556. package/dist/src/pr-identity.js.map +1 -0
  557. package/dist/src/prdelta.d.ts +181 -0
  558. package/dist/src/prdelta.d.ts.map +1 -0
  559. package/dist/src/prdelta.js +501 -0
  560. package/dist/src/prdelta.js.map +1 -0
  561. package/dist/src/pricing.d.ts +94 -0
  562. package/dist/src/pricing.d.ts.map +1 -0
  563. package/dist/src/pricing.js +209 -0
  564. package/dist/src/pricing.js.map +1 -0
  565. package/dist/src/priorfindings.d.ts +167 -0
  566. package/dist/src/priorfindings.d.ts.map +1 -0
  567. package/dist/src/priorfindings.js +445 -0
  568. package/dist/src/priorfindings.js.map +1 -0
  569. package/dist/src/priortasks.d.ts +31 -0
  570. package/dist/src/priortasks.d.ts.map +1 -0
  571. package/dist/src/priortasks.js +119 -0
  572. package/dist/src/priortasks.js.map +1 -0
  573. package/dist/src/profilelookup.d.ts +59 -0
  574. package/dist/src/profilelookup.d.ts.map +1 -0
  575. package/dist/src/profilelookup.js +98 -0
  576. package/dist/src/profilelookup.js.map +1 -0
  577. package/dist/src/profileoverrides.d.ts +91 -0
  578. package/dist/src/profileoverrides.d.ts.map +1 -0
  579. package/dist/src/profileoverrides.js +313 -0
  580. package/dist/src/profileoverrides.js.map +1 -0
  581. package/dist/src/profiles.d.ts +144 -0
  582. package/dist/src/profiles.d.ts.map +1 -0
  583. package/dist/src/profiles.js +604 -0
  584. package/dist/src/profiles.js.map +1 -0
  585. package/dist/src/promptdoc.d.ts +3 -0
  586. package/dist/src/promptdoc.d.ts.map +1 -0
  587. package/dist/src/promptdoc.js +18 -0
  588. package/dist/src/promptdoc.js.map +1 -0
  589. package/dist/src/prtarget.d.ts +70 -0
  590. package/dist/src/prtarget.d.ts.map +1 -0
  591. package/dist/src/prtarget.js +181 -0
  592. package/dist/src/prtarget.js.map +1 -0
  593. package/dist/src/recheckorder.d.ts +32 -0
  594. package/dist/src/recheckorder.d.ts.map +1 -0
  595. package/dist/src/recheckorder.js +5 -0
  596. package/dist/src/recheckorder.js.map +1 -0
  597. package/dist/src/reviewapi.d.ts +207 -0
  598. package/dist/src/reviewapi.d.ts.map +1 -0
  599. package/dist/src/reviewapi.js +939 -0
  600. package/dist/src/reviewapi.js.map +1 -0
  601. package/dist/src/reviewbotfindings.d.ts +33 -0
  602. package/dist/src/reviewbotfindings.d.ts.map +1 -0
  603. package/dist/src/reviewbotfindings.js +80 -0
  604. package/dist/src/reviewbotfindings.js.map +1 -0
  605. package/dist/src/reviewconfig.d.ts +753 -0
  606. package/dist/src/reviewconfig.d.ts.map +1 -0
  607. package/dist/src/reviewconfig.js +805 -0
  608. package/dist/src/reviewconfig.js.map +1 -0
  609. package/dist/src/reviewdelta.d.ts +51 -0
  610. package/dist/src/reviewdelta.d.ts.map +1 -0
  611. package/dist/src/reviewdelta.js +119 -0
  612. package/dist/src/reviewdelta.js.map +1 -0
  613. package/dist/src/reviewdest.d.ts +86 -0
  614. package/dist/src/reviewdest.d.ts.map +1 -0
  615. package/dist/src/reviewdest.js +142 -0
  616. package/dist/src/reviewdest.js.map +1 -0
  617. package/dist/src/reviewerstub.d.ts +12 -0
  618. package/dist/src/reviewerstub.d.ts.map +1 -0
  619. package/dist/src/reviewerstub.js +28 -0
  620. package/dist/src/reviewerstub.js.map +1 -0
  621. package/dist/src/reviewhost.d.ts +58 -0
  622. package/dist/src/reviewhost.d.ts.map +1 -0
  623. package/dist/src/reviewhost.js +135 -0
  624. package/dist/src/reviewhost.js.map +1 -0
  625. package/dist/src/reviewpost.d.ts +79 -0
  626. package/dist/src/reviewpost.d.ts.map +1 -0
  627. package/dist/src/reviewpost.js +233 -0
  628. package/dist/src/reviewpost.js.map +1 -0
  629. package/dist/src/reviewrender.d.ts +154 -0
  630. package/dist/src/reviewrender.d.ts.map +1 -0
  631. package/dist/src/reviewrender.js +480 -0
  632. package/dist/src/reviewrender.js.map +1 -0
  633. package/dist/src/reviewstate.d.ts +62 -0
  634. package/dist/src/reviewstate.d.ts.map +1 -0
  635. package/dist/src/reviewstate.js +167 -0
  636. package/dist/src/reviewstate.js.map +1 -0
  637. package/dist/src/reviewstore.d.ts +17 -0
  638. package/dist/src/reviewstore.d.ts.map +1 -0
  639. package/dist/src/reviewstore.js +105 -0
  640. package/dist/src/reviewstore.js.map +1 -0
  641. package/dist/src/round-progress.d.ts +120 -0
  642. package/dist/src/round-progress.d.ts.map +1 -0
  643. package/dist/src/round-progress.js +172 -0
  644. package/dist/src/round-progress.js.map +1 -0
  645. package/dist/src/round-stage-deadlines.d.ts +12 -0
  646. package/dist/src/round-stage-deadlines.d.ts.map +1 -0
  647. package/dist/src/round-stage-deadlines.js +10 -0
  648. package/dist/src/round-stage-deadlines.js.map +1 -0
  649. package/dist/src/session-command.d.ts +19 -0
  650. package/dist/src/session-command.d.ts.map +1 -0
  651. package/dist/src/session-command.js +20 -0
  652. package/dist/src/session-command.js.map +1 -0
  653. package/dist/src/settledread.d.ts +61 -0
  654. package/dist/src/settledread.d.ts.map +1 -0
  655. package/dist/src/settledread.js +135 -0
  656. package/dist/src/settledread.js.map +1 -0
  657. package/dist/src/severity.d.ts +167 -0
  658. package/dist/src/severity.d.ts.map +1 -0
  659. package/dist/src/severity.js +364 -0
  660. package/dist/src/severity.js.map +1 -0
  661. package/dist/src/severityrank.d.ts +8 -0
  662. package/dist/src/severityrank.d.ts.map +1 -0
  663. package/dist/src/severityrank.js +16 -0
  664. package/dist/src/severityrank.js.map +1 -0
  665. package/dist/src/standing-review.d.ts +45 -0
  666. package/dist/src/standing-review.d.ts.map +1 -0
  667. package/dist/src/standing-review.js +43 -0
  668. package/dist/src/standing-review.js.map +1 -0
  669. package/dist/src/storerows.d.ts +225 -0
  670. package/dist/src/storerows.d.ts.map +1 -0
  671. package/dist/src/storerows.js +7 -0
  672. package/dist/src/storerows.js.map +1 -0
  673. package/dist/src/styleguide.d.ts +25 -0
  674. package/dist/src/styleguide.d.ts.map +1 -0
  675. package/dist/src/styleguide.js +272 -0
  676. package/dist/src/styleguide.js.map +1 -0
  677. package/dist/src/sweepstatus.d.ts +42 -0
  678. package/dist/src/sweepstatus.d.ts.map +1 -0
  679. package/dist/src/sweepstatus.js +97 -0
  680. package/dist/src/sweepstatus.js.map +1 -0
  681. package/dist/src/templates.d.ts +243 -0
  682. package/dist/src/templates.d.ts.map +1 -0
  683. package/dist/src/templates.js +847 -0
  684. package/dist/src/templates.js.map +1 -0
  685. package/dist/src/types.d.ts +639 -0
  686. package/dist/src/types.d.ts.map +1 -0
  687. package/dist/src/types.js +90 -0
  688. package/dist/src/types.js.map +1 -0
  689. package/dist/src/unifieddiff.d.ts +22 -0
  690. package/dist/src/unifieddiff.d.ts.map +1 -0
  691. package/dist/src/unifieddiff.js +176 -0
  692. package/dist/src/unifieddiff.js.map +1 -0
  693. package/dist/src/yieldloop.d.ts +2 -0
  694. package/dist/src/yieldloop.d.ts.map +1 -0
  695. package/dist/src/yieldloop.js +14 -0
  696. package/dist/src/yieldloop.js.map +1 -0
  697. package/package.json +60 -4
@@ -0,0 +1,1099 @@
1
+ # quickreviewd — EXAMPLE config. Copy to config.toml and fill in.
2
+ #
3
+ # Every runtime knob lives here (spec §2). The values below are safe defaults:
4
+ # dry_run is ON (every GitHub mutation is recorded to state/dryrun/ instead of
5
+ # executed) and every identity is empty. The ~10 values a new operator must
6
+ # supply are marked "← FILL (n)" — grep for FILL. Flip dry_run to false only
7
+ # as a deliberate cutover act, never as part of setup.
8
+ #
9
+ # YOU DO NOT HAVE TO FILL THIS BY HAND: `scripts/qcr onboard` interviews you
10
+ # for the few values only you can answer, derives the rest, and patches this
11
+ # file in place — every comment survives, and a re-run edits only what changed.
12
+ # It never writes dry_run in either direction; going live is its own explicit
13
+ # step. See docs/onboarding.md.
14
+ # ONE mutation class is exempt and executes for real even here: the App
15
+ # re-pointing its OWN webhook URL (see [ingress] below) — it can never touch
16
+ # the reviewed repo, and every execution is logged loudly.
17
+
18
+ dry_run = true
19
+
20
+ # The operations timezone: whose calendar day the system runs on.
21
+ # Every day-scoped feature (the [feedback] same-day addendum + its daily
22
+ # expiry, metrics day buckets, TUI alert memory, companion day rollover)
23
+ # derives its day from this one knob; [eod] and [slack.policy] `timezone`
24
+ # default from it and may still be overridden in their own sections.
25
+ [ops]
26
+ timezone = "UTC" # ← FILL: your IANA timezone, e.g. "Europe/Warsaw"
27
+
28
+ [github]
29
+ repo = "" # ← FILL (1): the repo to review, "owner/name"
30
+ # Whose GitHub credential the daemon runs on:
31
+ # "app" (the default; a missing key means "app") — a GitHub App: its
32
+ # installation token, webhooks, APPROVE / REQUEST_CHANGES reviews.
33
+ # "user" — your OWN GitHub account, no App. Polling only: [ingress] shape
34
+ # must be "polling" (config load refuses anything else), no webhooks,
35
+ # no webhook self-heal. Every review posts as a COMMENT whose body
36
+ # states the verdict (an automated verdict never counts as your
37
+ # approval, and GitHub refuses either on your own PR). The token is
38
+ # read from the FILE <secrets dir>/github_token (0600, the token
39
+ # alone), which `qcr github-token sync` writes from your `gh` login;
40
+ # never from the environment or a command line. app_slug may be "";
41
+ # bot_login is your own GitHub login. The App secrets (app_id,
42
+ # app.pem, webhook_secret) are not needed.
43
+ #auth = "app"
44
+ app_slug = "" # ← FILL (2): your GitHub App slug ("" under auth = "user")
45
+ bot_login = "" # ← FILL (3): the App bot login, "<app_slug>[bot]" (your login under auth = "user")
46
+ review_team = "" # ← FILL (4): "org/team" whose review request triggers
47
+ # There is no reviewer knob here. The reviewer never hands a PR back: it always
48
+ # decides on the merits and posts a verdict. If you want a named person told
49
+ # about a particular KIND of change, that is the optional [escalation] section
50
+ # further down — off by default.
51
+ trigger_label = "AI review requested"
52
+ in_progress_label = "AI review in progress"
53
+ reviewed_label = "Automatic Review"
54
+ improvement_label = "Bot improvement"
55
+ issue_label = "bot-filed"
56
+ # secrets dir: app_id, app.pem, webhook_secret, tunnel_token (auth = "user": github_token instead
57
+ # of the first three); optional openai_api_key (pi, API-key
58
+ # route) and openai_embeddings_api_key (only with embedding_provider = "openai" in the openviking table)
59
+ # "" (recommended) = the install's config home, $XDG_CONFIG_HOME/qcr/<install>/secrets (the old
60
+ # ~/.config/quickreview-bot is never read — `qcr install migrate` moves it). A path here wins.
61
+ secrets_dir = ""
62
+ # The repo's default branch: the improvement filer's PR base, the
63
+ # replay diff base, the CLI-ops fixture base. PR review rounds always use each
64
+ # PR's own base_ref from the GitHub API, never this.
65
+ base_branch = "main" # ← FILL: your default branch ("main"/"master")
66
+ # Base-clone directory name under the sandbox root. "" (recommended) derives
67
+ # it from the repo slug's basename — "owner/name" ⇒ "name".
68
+ clone_dir = ""
69
+
70
+ [owner]
71
+ # The system's ONE accountable human, as a configured role. Every
72
+ # identity surface renders from here: Slack prose, the companion day-open
73
+ # labels, the pinned-memory head framing, the escalation-mention fallback.
74
+ # Empty values degrade to neutral wording ("the owner" / "the operator").
75
+ name = "" # ← FILL: the operator's display name for rendered prose
76
+ slack_id = "" # ← FILL (if enabling Slack): U…; required then, and a [slack.users] key
77
+ # (legacy alias on input: [slack] legacy_owner_slack_id)
78
+ github_login = "" # ← FILL: operator GitHub login; escalation-mention fallback
79
+
80
+ [server]
81
+ hostname = "" # ← FILL (6): the public webhook hostname.
82
+ # Leave empty when your ingress assigns
83
+ # its own (the quick tunnel does) — then
84
+ # [ingress] below carries the shape.
85
+ port = 8787 # loopback only; your HTTPS ingress fronts it
86
+ # (a tunnel sidecar, cloudflared on the
87
+ # host, or any reverse proxy)
88
+
89
+ # [ingress] — webhook-URL self-heal, for tunnels whose hostname
90
+ # ROTATES (the laptop quick-tunnel route). The tunnel sidecar writes
91
+ # its assigned bare hostname (e.g. "abc.trycloudflare.com") to `hostname_file`;
92
+ # the daemon watches it and re-points the GitHub App's own webhook URL on
93
+ # change (JWT-authed PATCH /app/hook/config, read-back verified). This is the
94
+ # ONE mutation class that executes even in dry_run (AGENTS.md invariant 6) — loudly
95
+ # logged, structurally closed to this one kind; it
96
+ # touches only the App's own configuration, never the reviewed repo. Off by
97
+ # default: with the section absent (or shape = "none") no watcher ever starts —
98
+ # a stable hostname (named tunnel, VM route) needs none of this.
99
+ # shape = "polling" takes no webhooks at all: the daemon's /webhook route
100
+ # refuses every delivery, no webhook_secret is read, and the reconcile poll
101
+ # (poll_interval_s, below) is the only trigger. [github] auth = "user"
102
+ # requires it.
103
+ #[ingress]
104
+ #shape = "tunnel-file" # "none" (default) | "tunnel-file" | "polling"
105
+ #hostname_file = "" # "" ⇒ <root>/state/ingress/hostname
106
+ #debounce_ms = 3000 # settle time after a file change
107
+ #sweep_interval_s = 300 # polling backstop behind fs.watch
108
+
109
+ [daemon]
110
+ poll_interval_s = 300
111
+ # Rounds are network-IO-bound (model calls), not CPU-bound; the container's
112
+ # 2 CPU / 8 GB Docker cap is the hard resource guard.
113
+ max_concurrent_rounds = 12
114
+ round_deadline_min = 75
115
+ # A round overdue with no verdict whose session is dead is
116
+ # self-requeued by the daemon (fresh dispatch to the PR's own session) at most
117
+ # this many times per PR head. Past the cap the round is abandoned as an
118
+ # anomaly and the PR RETURNS TO ITS STANDING-VERDICT STATE — never an approval,
119
+ # and never a park: there is no "awaiting human" state.
120
+ max_self_requeues = 2
121
+ # Lost-prompt net: a running round whose inject file was consumed but whose
122
+ # session shows NO activity since the round started is re-queued (once per
123
+ # head, inside the max_self_requeues cap) after this many minutes — well before
124
+ # the deadline, so a dropped round prompt does not cost a full deadline window.
125
+ round_stall_grace_min = 10
126
+ # Durable daemon→Pi stage leases. Start is short because no model
127
+ # work exists yet; running is a fixed hard cap above the measured 23.4m live
128
+ # maximum and below the 75m defense-in-depth round deadline. Unknown evidence
129
+ # gets one minute once, never an open-ended extension.
130
+ session_command_start_lease_s = 120
131
+ session_command_running_max_min = 45
132
+ # Command-correlated execution milestones: no first pass, incomplete pass plan,
133
+ # and all-passes-done/no-verdict. Absolute start-derived bounds; activity cannot
134
+ # renew them.
135
+ round_first_pass_max_min = 5
136
+ round_passes_max_min = 35
137
+ round_finalize_grace_min = 5
138
+ # Once a round's review is CONFIRMED posted, a stage limit does not cancel
139
+ # it — the session's tail (ledger, findings record, verdict
140
+ # file) gets this long after the posting, and a round still without a verdict
141
+ # then is settled from the posting receipt. Never a requeue, never a second
142
+ # posting.
143
+ round_posted_tail_grace_min = 15
144
+ # A follow-up round whose pre-approval sweep is owed (a whole
145
+ # round-1-style plan after the delta's) gets this many extra minutes for its
146
+ # passes and its hard cap.
147
+ # follow_up_sweep_extra_min = 20
148
+ posting_receipt_grace_s = 120
149
+ # A review POST GitHub answers 5xx (or not at all) is handed to the daemon
150
+ # (receipt `deferred`) and retried at these minutes after the first failure,
151
+ # each attempt through the gate after a marker read; past the window the PR
152
+ # goes FAILED_LOUD (u to recover).
153
+ # posting_retry_offsets_min = [4, 12, 36]
154
+ # posting_retry_window_min = 60
155
+ session_command_unknown_grace_s = 60
156
+ session_command_unknown_max_checks = 1
157
+ session_command_max_start_retries = 2
158
+ # pi refuses every prompt while it compacts, so a command queued
159
+ # behind a compaction the session's inject bridge reports running is late, not
160
+ # lost — its start lease is held open for up to this long. Past it, the
161
+ # bounded same-session redelivery (max_prompt_redeliveries), and only then the
162
+ # kill-and-replay recovery, take over.
163
+ session_compaction_max_s = 600
164
+ # The central lens permit broker (bot workers): the most lens sessions alive at
165
+ # once across EVERY worker, granted first-in first-out. 20 is the VM's ruling
166
+ # (the CPU gate measured 20 sessions fine on 2 cores). Grants are spaced at
167
+ # least lens_grant_stagger_ms apart so a full wave never creates its sessions
168
+ # in one burst (the gate measured a 0.7-2.5 s loop stall for 8-20 at once);
169
+ # 0 turns the spacing off. status.json `permits` shows the use and the queue.
170
+ lens_permits = 20
171
+ lens_grant_stagger_ms = 250
172
+ # The idle-PR answer worker: an operator's message to a PR with no running
173
+ # worker is answered at once by a short worker that resumes the PR's session,
174
+ # replies and exits. It is ended at answer_timeout_s of wall clock and stops at
175
+ # answer_cap_usd of its own spend. It posts only when the message carried
176
+ # may_post (the viewer's Alt-p, `qcr message --may-post`), through the one gate.
177
+ answer_timeout_s = 600
178
+ answer_cap_usd = 2
179
+ round_stage_supervisor_stale_s = 180
180
+ # The daemon watches its own event loop (status.json
181
+ # `daemon.event_loop`, `qcr status`, the TUI board). One delay of
182
+ # event_loop_stall_max_s or more, or a p99 of event_loop_stall_p99_s or more
183
+ # over a window its samples cover, is a stall: it wakes the overseer
184
+ # (`daemon_event_loop_stalled`) at most once per cooldown, naming the cause —
185
+ # "the loop itself blocked" when the daemon itself was on CPU for most of the
186
+ # stall (the stall's own evidence wins; any throttling is named beside it);
187
+ # else "CPU starved" when the container (/sys/fs/cgroup/cpu.stat, over the
188
+ # same window) was throttled in event_loop_starved_throttle_ratio of its CFS
189
+ # periods or more; else "cause unknown" (host contention or a blocking wait).
190
+ event_loop_window_s = 300
191
+ event_loop_stall_max_s = 10
192
+ event_loop_stall_p99_s = 2
193
+ event_loop_wake_cooldown_min = 60
194
+ event_loop_starved_throttle_ratio = 0.2
195
+ # Zombie-round expiry: a round stuck 'overdue' longer than this whose
196
+ # PR has since verdicted/closed (i.e. a rerun would never touch it) is expired.
197
+ # 0 disables the sweep.
198
+ expire_overdue_after_h = 24
199
+
200
+ [eod]
201
+ time = "18:00"
202
+ # timezone defaults from [ops]; uncomment to run the EOD sweep on another clock.
203
+ #timezone = "Europe/Warsaw"
204
+ # Daily overseer heartbeat: anomaly-table + digest review pass.
205
+ heartbeat_time = "09:00"
206
+ # Watch the owner's pinned entries across the sweep. Curation must never
207
+ # touch them; this makes the invariant detectable rather than merely
208
+ # instructed. The window closes early when the day's digest appears.
209
+ pinned_watchdog = true
210
+ pinned_watchdog_window_min = 90
211
+
212
+ [caps]
213
+ improvement_pr_max_lines = 50
214
+ improvement_pr_max_files = 3
215
+ improvement_pr_per_day = 1
216
+ improvement_pr_per_week = 3
217
+ issues_per_day = 3
218
+ # Paths a bot improvement PR may never touch. This value REPLACES
219
+ # the shipped default shown here — adjust to your repo's shape — but files
220
+ # named .env* are refused unconditionally regardless of this list.
221
+ improvement_pr_deny_prefixes = ["migrations/", "infra/", "ansible/", ".github/workflows/"]
222
+
223
+ [models]
224
+ # default for every role; tiered pass models below
225
+ # Provider prefix is `openai-codex/` — the ChatGPT-OAuth route's provider key
226
+ # in pi's auth.json. Any OpenAI login serves it: when pi holds no
227
+ # `openai-codex` login but an `openai` one (pi 1.4's Sign in with ChatGPT, or
228
+ # an OpenAI API key), the same model runs on `openai` (docs/llm-setup.md).
229
+ default = "openai-codex/gpt-6.1-sol"
230
+ conversation = "openai-codex/gpt-6.1-sol"
231
+ # Passes always run at sol-high — single variant, not raisable (already top).
232
+ risk_passes = ["pass-security", "pass-prod-risk", "pass-data-safety"]
233
+ # pass-validate's tier — its own configured tier (floored by a repo profile's
234
+ # min_tier for it), never raised to match a lens (src/dispatch.ts
235
+ # validatorTier). Shipped at sol-high: the validator is the adversarial gate
236
+ # over every finding. One validator call per round — not a hot-path multiplier.
237
+ validate_tier = "sol-high"
238
+ # Diffs under this many changed lines (additions+deletions) start at terra-med.
239
+ small_diff_lines = 150
240
+ # [models.pass_tiers] — per-lens tier pins: pass name → tier
241
+ # name (a key of [models.tiers]). SHIPPED DEFAULT (src/config.ts
242
+ # DEFAULT_PASS_TIERS): pass-baseline, pass-sweep and
243
+ # pass-behaviour-delta at "sol-high" (gpt-6.1-sol at high effort) — measured
244
+ # +6 defects on 18 AACR PRs for +18% cost per PR. An entry here replaces that
245
+ # lens's default pin (e.g. "pass-baseline" = "sol-med"); a repo profile lowers
246
+ # one lens's thinking with [lenses.<pass>] effort = "medium". A pinned lens
247
+ # runs at that tier on every PR whatever the diff size — the plan names it, it
248
+ # is not raisable, and every built variant of it carries that tier's spec. The
249
+ # validator never follows a pinned lens (it runs at validate_tier).
250
+ # Not for pass-validate (use validate_tier) nor for a risk pass (always
251
+ # sol-high). A profile min_tier still floors a pin. Unknown passes or tiers
252
+ # are refused at load. e.g.
253
+ # [models.pass_tiers]
254
+ # "pass-sweep" = "sol-high"
255
+ # "pass-baseline" = "sol-high"
256
+
257
+ [models.tiers]
258
+ # PassTier → model spec; the whole spec (incl. the `:thinking` suffix) rides
259
+ # the built agent's `model:` field — pi's --model accepts the suffix, and the
260
+ # native subagent extension reads only that field.
261
+ "terra-med" = "openai-codex/gpt-6.1-sol:medium"
262
+ "sol-med" = "openai-codex/gpt-6.1-sol:medium"
263
+ "sol-high" = "openai-codex/gpt-6.1-sol:high"
264
+
265
+ [models.pricing]
266
+ # Spend rates keyed by bare model id, USD per million tokens. Consumed by
267
+ # spend accounting; empty entries fall back to unpriced.
268
+ # "gpt-6-sol" = { input_per_mtok = 0.0, output_per_mtok = 0.0 }
269
+
270
+ [tui]
271
+ # qcr tui — the operator TUI on the owner's PC, polling the review VM over ssh.
272
+ # Read-only against the VM except the user's own keystrokes + inject files.
273
+ #
274
+ # target — where the daemon container lives relative to this TUI:
275
+ # "ssh" — the VM/server topology (the always-on recommendation): every
276
+ # poll, attach, digest read and evidence copy rides
277
+ # `ssh <ssh_host> …`, sharing one control master.
278
+ # "local" — the laptop topology (everything on THIS machine, e.g. the
279
+ # compose stack): the SAME commands run against local docker
280
+ # directly and no ssh process is ever spawned. `ssh_host` is
281
+ # ignored; `sudo_docker` still decides the sudo prefix. The
282
+ # memory forward follows this knob too — it reaches the daemon
283
+ # container's loopback memory engine with a plain `docker exec`
284
+ # (the forward script's --local mode) instead of the ssh hop.
285
+ # Onboarding writes this from the recorded topology. Until it has (ssh with an
286
+ # EMPTY ssh_host), the TUI never runs `ssh ""`: on a compose project (the
287
+ # checklist recorded the local topology, or the project .env pins
288
+ # QCR_VERSION) it reads the local stack and says so once; otherwise every
289
+ # surface says "ssh_host is empty — run qcr onboard".
290
+ target = "ssh"
291
+ ssh_host = "" # ← FILL (7): ssh alias of the VM running the container
292
+ # (leave "" when target = "local" — it is unused)
293
+ container = "quickreview-sandbox" # bundled local Compose: "quickreview-daemon-1" (onboarding fills this)
294
+ memory_container = "" # the container the memory forward relays into. "" = `container`
295
+ # (OpenViking is the daemon container's child). On a VM running
296
+ # the memory sidecar (the openviking table's endpoint =
297
+ # "remote") set "quickreview-memory"; flip it back with the endpoint.
298
+ remote_state_dir = "/opt/quickreview/state" # host path on ssh; inside the daemon container on local,
299
+ # where this default reads as the release image's /opt/qcr/state
300
+ # (the compose state volume; onboarding writes it for local)
301
+ remote_sandbox_root = "" # "" = read the container's qcr.layout label (release: /opt/qcr, legacy: /opt/quickreview/sandbox);
302
+ # a value here wins and is never probed
303
+ sudo_docker = true # docker on the VM needs sudo (root-owned socket). On local, onboarding
304
+ # writes false, and the TUI drops sudo by itself when this user can open
305
+ # the docker socket (a sudo prompt behind the TUI can only fail)
306
+ poll_interval_s = 3
307
+ stale_after_s = 90 # DATA STALE banner past this written_at age
308
+ # (the daemon rewrites status.json every ~30s)
309
+ msg_timeout_s = 30 # quick-message: pending → undelivered after this
310
+ evidence_timeout_s = 60 # evidence copier: per-remote-read
311
+ # budget. A wedged ssh costs one sweep — the
312
+ # copy is retried on the next poll, never
313
+ # blocking the poll or the paint loop.
314
+ evidence_why_interval_s = 10 # evidence sweep pace: at most one
315
+ # `qcr why` per this many seconds (gap from
316
+ # the end of the last one), clamped 5–600 s.
317
+ # No `why` at all while the daemon is draining
318
+ # or its heartbeat is stale.
319
+
320
+ [pi]
321
+ # VERIFY-ON-PIN: package + version mirrored in package.json "qcrPi" block and the
322
+ # ansible group_vars; the image never floats.
323
+ package = "@earendil-works/pi-coding-agent"
324
+ version = "1.4.0"
325
+ session_dir = "state/pi-sessions"
326
+ # The lens settings that used to live here (subagent_max_concurrency,
327
+ # subagent_cache_group, subagent_cache_stagger_ms, subagent_pass_deadline_min)
328
+ # moved to [engine] (lens_max_concurrency, lens_cache_group,
329
+ # lens_cache_stagger_ms, lens_pass_deadline_min); a config that still sets them
330
+ # is refused.
331
+ # Who launches a round's lenses: "engine", our lens tool and discovery wave on
332
+ # an SDK session, the only value ("native", pi's own subagent glue, is retired
333
+ # and refused).
334
+ # lens_runtime = "engine"
335
+ # The round's engine starts the planned discovery wave itself at round start
336
+ # (rule 9) and the main session joins it with lens_wave. false = the main
337
+ # session launches the wave, with the same tools.
338
+ # engine_wave = true
339
+
340
+ # What hosts a pi session on the review, bench and scan routes: "sdk" runs it
341
+ # in-process on the pi SDK (the default and the only value; "proc", pi as a
342
+ # child process, is retired and refused). `qcr review` and the bench (`qcr-bench
343
+ # review`, `defect-judge`) host the SDK: in the bench each case, and each case
344
+ # of the judge's pool, runs in a host process of its own, so one crash or frozen
345
+ # loop costs one case, and `[pi] lens_runtime = "engine"` gives the review our
346
+ # lens tool. A route that has no host yet refuses "sdk".
347
+ # In the image the SDK runs the model's bash as the separate tool uid and
348
+ # fences the file tools off /proc, /run/qcr-in and the agent dir.
349
+ # The scan runs its leaves on the SDK inside its own detached driver process
350
+ # (`qcr scan start`), never inside the companion's pi; a scan the companion
351
+ # drives itself refuses.
352
+ # The lens tool's settings ride here too (docs/prompt-caching.md):
353
+ # lens_max_concurrency cap on running lens sessions per round (default 4);
354
+ # the daemon's [daemon] lens_permits is the bot's
355
+ # cross-worker budget
356
+ # lens_cache_group lens sessions per shared provider prompt-cache key
357
+ # (default 5; 0 = a random id each, no cache hits)
358
+ # lens_cache_stagger_ms how long a group's later sessions wait after its
359
+ # first, so the cache is written first (default 2500)
360
+ # lens_pass_deadline_min per-pass deadline in minutes: a pass still running
361
+ # is aborted and retried ONCE, and a second timeout
362
+ # fails that pass alone; 0 = no deadline (default 8)
363
+ # [engine]
364
+ # runtime = "sdk"
365
+ # lens_max_concurrency = 4
366
+ # lens_cache_group = 5
367
+ # lens_cache_stagger_ms = 2500
368
+ # lens_pass_deadline_min = 8
369
+
370
+ [memory]
371
+ # Round seeding — how much memory rides each round prompt and how
372
+ # long the daemon waits for it. Memory NEVER blocks or delays a round: past
373
+ # the deadline the round dispatches memory-less and logs the gap.
374
+ context_tokens = 2000
375
+ # The seed's whole budget, in ms (clamped 100…15000). One document embedding
376
+ # on a 2-CPU VM takes 3 s or more, and a seed's query queued behind one needs
377
+ # room for it. It costs at most
378
+ # a few seconds on a ~14-minute round. Keep it above the search's own
379
+ # search_timeout_ms (the openviking block, below).
380
+ context_timeout_ms = 9000
381
+ # The embedding tail of one written finding case, in ms (clamped
382
+ # 0…30000, 0 = off): the server goes on embedding a case batch after its
383
+ # write returns, so a round seed that starts within (cases × this) of the
384
+ # write waits that out before it sends its search — inside context_timeout_ms,
385
+ # never past it, keeping 3 s of the budget for the search itself. While any
386
+ # review round runs or is queued, the case writer also holds its backfill,
387
+ # its late activity and every case those two owe (only cases the capture
388
+ # pass's sync owes are written meanwhile).
389
+ case_embed_tail_ms = 4000
390
+ # Memory health (src/memory/health.ts) — how an outage reaches a
391
+ # person. Every probe_interval_s the daemon lists the reviewer memories root
392
+ # AUTHENTICATED, once with its own key and once with the sessions' ovcli.conf.
393
+ # A refused credential wakes memory_auth_failed at once (never graced, never
394
+ # held by a drain); no authenticated answer for down_after_min wakes
395
+ # memory_down (a server still warming up reads "unknown" for boot_grace_min;
396
+ # a drain holds the wake until it lifts). Either re-wakes every rewake_h while
397
+ # it lasts. The state rides status.json `memory`: the TUI board banner, its
398
+ # desktop notification, `qcr status`, and ONE tracker ticket the TUI files for
399
+ # an incident older than incident_ticket_after_min.
400
+ probe_interval_s = 120
401
+ down_after_min = 10
402
+ boot_grace_min = 5
403
+ rewake_h = 6
404
+ incident_ticket_after_min = 120
405
+ # Writes that stick: every [feedback] signal is ledgered with
406
+ # its memory state, and whatever memory has not taken yet the backfill pump
407
+ # writes every backfill_interval_s (at most 20 a tick, only while memory
408
+ # reads healthy). A signal owed for longer than write_backlog_after_min wakes
409
+ # memory_write_backlog (and again every rewake_h while the backlog lasts). A
410
+ # signal memory refused 12 times is given up on, loudly, and queued again at
411
+ # the daemon's next start and once a day.
412
+ backfill_interval_s = 60
413
+ write_backlog_after_min = 120
414
+ # Finding cases (src/cases.ts): one document per posted finding a
415
+ # human answered or whose fate is known, in reviewer memory's `finding-cases/` — the
416
+ # learner's context, searched by the EOD curator and the companion, never by a
417
+ # review round. Every cases_interval_s the daemon works out from state.db which
418
+ # cases are new or changed and, only while memory reads healthy, writes up to 2
419
+ # (one batch-write — each case is ~3.4 s on the VM's one-thread embedder — and
420
+ # never started while a review round is reading memory) and
421
+ # backfills two more PRs from the last cases_backfill_days days (resumable; 0
422
+ # turns the backfill off).
423
+ cases_interval_s = 120
424
+ cases_backfill_days = 60
425
+ # The learner (src/learner/): lessons_mine_lead_min before the EOD
426
+ # sweep (and on `qcr lessons mine`) the daemon mines the human outcomes of the
427
+ # last lessons_mine_days — refuted / contested findings, the [feedback]
428
+ # ledger, human comments and REQUEST_CHANGES on PRs the bot approved, recheck
429
+ # flips, and commits that changed a bot-approved PR's lines within
430
+ # lessons_recall_window_days of its merge — into lesson CANDIDATES. The EOD
431
+ # sweep is handed them as state/eod/<day>/outcomes.md (at most
432
+ # lessons_packet_new_max new a day, misses first; every undecided one is
433
+ # carried; undecided after lessons_expiry_ops_days it expires, recorded) and
434
+ # answers with an `eod-curation-receipt`. No receipt within
435
+ # lessons_receipt_wait_min wakes eod_curation_missing; a refused or unfound
436
+ # lesson wakes eod_lessons_not_persisted. The digests' "Learnings" from
437
+ # lessons_digest_backfill_from..to (written while memory refused every write)
438
+ # become candidates once.
439
+ lessons_mine_lead_min = 15
440
+ lessons_mine_days = 45
441
+ lessons_receipt_wait_min = 180
442
+ lessons_expiry_ops_days = 7
443
+ lessons_packet_new_max = 40
444
+ lessons_recall_window_days = 14
445
+ lessons_recurrence_days = 90
446
+ lessons_digest_backfill_from = "2026-08-31"
447
+ lessons_digest_backfill_to = "2026-10-02"
448
+ # How curated lessons and past finding cases reach a review.
449
+ # "off" — nothing runs: no selection, no shadow ledger.
450
+ # "shadow" — (the shipped value) every round selects the lessons and cases
451
+ # it WOULD carry, through OpenViking — one prefetch per round
452
+ # (two finds: one for the lessons, one for the cases), started
453
+ # after the round's handoff so it never delays the round, at
454
+ # most shadow_budget_ms (≤ 20000) — and the shadow ledger records
455
+ # them, plus every posted finding a delivered dont-raise lesson
456
+ # would have touched. No prompt, task file or agent changes.
457
+ # "on" — delivery: the same selection, awaited beside the round's
458
+ # memory seed within delivery_budget_ms (≤ 2000), reaches each
459
+ # lens's task file, the lessons files of the validator and of
460
+ # any lens the session adds, the main session's "Lessons in
461
+ # force" index and past-review cases (the bleed rule
462
+ # reworded), the validator's demotion-only `lesson-shape`
463
+ # addendum, and the repo facts for the paths a recheck or an
464
+ # author reply touches. A round that selects nothing renders
465
+ # exactly as under "off".
466
+ # Read `qcr lessons shadow-report` (and the `lessons` clause of `qcr status`)
467
+ # before switching to "on". The operator's kill switch sits beside this one:
468
+ # `qcr lessons off|on` and `qcr lessons suppress|unsuppress <lesson>`, through
469
+ # the root-sealed commands/operator/ channel only, stored in state.db.
470
+ delivery = "shadow"
471
+ delivery_budget_ms = 2000
472
+ shadow_budget_ms = 10000
473
+ # HOW MANY items the round's one context search may draw from each area it
474
+ # searches. `context_tokens` above is the budget; this is the corpus — the two
475
+ # bind independently.
476
+ #
477
+ # A round searches `experiences/` ONLY — the curated lessons of the memory
478
+ # layout (src/memory/layout.ts). Ops notes and feedback events live in `events/`,
479
+ # the owner's unpinned instructions in `preferences/` (the pinned ones ride the
480
+ # round's guaranteed head in full), and none of them belongs in a round's
481
+ # recalled context; nor do the built-in `skills`/`resources` overview blurbs
482
+ # that take most of the block when no quotas are set.
483
+ # Any other key here is IGNORED with one log line, and `experiences` defaults
484
+ # to 24 when it is missing. The count is generous on purpose — its job is to
485
+ # name the area, and `context_tokens` decides how much rides the prompt.
486
+ context_quotas = { experiences = 24 }
487
+
488
+ [openviking]
489
+ # OpenViking (github.com/volcengine/OpenViking) — the review system's context
490
+ # store, running as a SECOND process inside the quickreview container on
491
+ # LOOPBACK :1933. The daemon supervises it as a child process
492
+ # (restart + backoff, stdout folded into the daemon log) and reports its
493
+ # health, version, RSS and CPU in the heartbeat.
494
+ #
495
+ # TRIPLE-MIRRORED PINS, same discipline as [pi]: `version` and
496
+ # `llama_cpp_python_version` here == deploy/ansible/group_vars/all.yml
497
+ # (openviking_version / llama_cpp_python_version) == the Dockerfile venv layer,
498
+ # which installs BOTH by referencing those group_vars — never a literal.
499
+ # tests/openviking.test.ts fails on any drift; the heartbeat drift-checks the
500
+ # running server's self-reported /health version against `version` too.
501
+ #
502
+ # Every path below is a CONTAINER path. On a dev PC the binary is absent: the
503
+ # supervisor logs once, reports unavailable, and never crash-loops.
504
+ enabled = true
505
+ # Who runs the server. "local" (the default): the daemon supervises it, and the
506
+ # local embedder, as children of this container. "remote": the memory sidecar
507
+ # (docker/memory/) runs both on `host`:`port` — set host to the sidecar's name
508
+ # on the container network — and the daemon only probes its health. The sidecar
509
+ # owns the boot provisioning and the embedder re-embed. Never publish its port.
510
+ endpoint = "local"
511
+ version = "0.4.15"
512
+ # The vendored pi extension's upstream base commit (extensions/vendor/
513
+ # openviking-pi). Pinned beside the server version so a bump re-verifies the
514
+ # vendored patch series rather than silently rebasing onto new upstream behaviour.
515
+ extension_commit = "d436baa23c83938b22bb0aab08b0ce48568b5ff4"
516
+ # The daemon and every review session act as this user, holding a key the
517
+ # server confines to it — that confinement is what keeps companion memory out
518
+ # of a posted review. NEVER an ADMIN or ROOT key: ADMIN grants no cross-user
519
+ # data access anyway, and can list every user key in plaintext.
520
+ user = "reviewer"
521
+ account = ""
522
+ reviewer_key_file = "openviking_reviewer_key"
523
+ # ov.conf `server.auth_mode`. `api_key` is the required posture: the
524
+ # server confines each USER key to its own tree, and that confinement is the
525
+ # only thing keeping companion memory out of a posted review. `dev` bypasses
526
+ # authentication on loopback, which makes the two-user design nominal.
527
+ # MIRRORED into group_vars (openviking_auth_mode), which the entrypoint renders; tests/openviking.test.ts
528
+ # and the scripts/deploy.sh preflight both refuse on drift.
529
+ auth_mode = "api_key"
530
+ # The SERVER's own root key, by file name under the secrets mount. ov.conf
531
+ # needs it to run in api_key mode and boot provisioning needs it to mint users.
532
+ # It is the server's credential, never a client's: no daemon surface, no review
533
+ # session and no companion process ever holds it (writeOvcliConf refuses it as
534
+ # a session credential; packages/pi-plugin/extensions/bash-guard.ts refuses to read the files that
535
+ # carry it). Mirrored as openviking_root_key_file.
536
+ root_key_file = "openviking_root_key"
537
+ # The users boot provisioning ENSURES exist — created if absent, left exactly
538
+ # as they are if present, and NEVER rotated (a rotation orphans the keys
539
+ # already distributed to the companion's PC). Mirrored as
540
+ # openviking_provision_users (comma-joined).
541
+ provision_users = ["reviewer", "companion"]
542
+ request_timeout_ms = 10000
543
+ # The round seed's one context search, in ms (clamped 100…15000) — above one
544
+ # document embedding's time, and below
545
+ # [memory] context_timeout_ms, and a seed that sent its search late passes
546
+ # the time it has left, which wins when shorter.
547
+ search_timeout_ms = 8000
548
+ default_category = "experiences"
549
+ read_cache_ttl_ms = 15000
550
+ # NOT an openviking dependency — an undeclared optional extra. Without it the
551
+ # default local dense embedder raises EmbeddingConfigurationError at startup,
552
+ # so the official image cannot boot (verified against a running 0.4.15 server).
553
+ # It is also the runtime of the local English embedder (below).
554
+ llama_cpp_python_version = "0.3.35"
555
+ host = "127.0.0.1" # loopback ONLY — the port is never published
556
+ port = 1933
557
+ bin = "/opt/openviking/venv/bin/openviking-server"
558
+ # ~/.openviking is symlinked here at boot: memory lives on the STATE VOLUME so
559
+ # it survives an image rebuild. config_file/workspace derive from this.
560
+ home = "/opt/quickreview/state/openviking"
561
+ # config_file = "" # empty ⇒ <home>/ov.conf
562
+ # workspace = "" # empty ⇒ <home>/workspace
563
+ # ov.conf is TEMPLATED at boot by the entrypoint — the interactive
564
+ # `openviking-server init` wizard is never invoked in-container.
565
+ workers = 1 # ov.conf server.workers, 2-CPU cap
566
+ queue_concurrency = 2 # accepted, unused: the entrypoint takes ov.conf's queue concurrency from the deploy (group_vars openviking_queue_concurrency)
567
+ # Health gate is /ready, NEVER `doctor`: doctor makes live provider calls and
568
+ # exits 1 when no `vlm` is configured, which is our normal state. /health is
569
+ # unauthenticated and carries the version the drift check reads. The heartbeat
570
+ # reports the CACHED sample, so writing status.json never waits on the network.
571
+ health_interval_s = 30
572
+ # The /health + /ready probe and the local embedder's /health, in ms (clamped
573
+ # 100…20000). /ready runs an embedding and queues behind a case batch's
574
+ # embeddings, so a shorter limit flaps /ready under load. Memory health's
575
+ # "serving" half reads this probe.
576
+ health_timeout_ms = 6000
577
+ # The supervisor never gives up: a memory engine that stays down is worse than
578
+ # a slow retry, and every call site already degrades memory-less.
579
+ restart_backoff_ms = [1000, 2000, 5000, 15000, 30000, 60000]
580
+ restart_reset_after_s = 120 # an uptime this long resets the ladder
581
+ # The embedder. LOCAL is the shipped default: a small
582
+ # ENGLISH embedding model (bge-small-en-v1.5, 384 dims) served on LOOPBACK
583
+ # inside the container by scripts/openviking-embedder.py, over the pinned
584
+ # llama-cpp-python — so nothing but the model call ever leaves the machine,
585
+ # memory text included. ov.conf reaches it through the engine's
586
+ # OpenAI-COMPATIBLE client (provider "openai", api_base http://127.0.0.1:<port>/v1,
587
+ # no key): that client is asynchronous, so a search does not stall the
588
+ # server. Hosted embeddings are an explicit opt-in: embedding_provider =
589
+ # "openai" with model "text-embedding-3-small" / dimension 1536, and the
590
+ # credential in the secrets dir under `embedding_key_file` — a SEPARATE file
591
+ # from pi's `openai_api_key`, so either can be set without the other. ov.conf
592
+ # carries NO `vlm` section either way: extraction, summaries and digest are off
593
+ # (memories are authored, never extracted).
594
+ # Changing the embedder changes the vector space: the boot detects it, moves
595
+ # the old vectors aside and re-embeds reviewer memory (docs/memory-embeddings.md).
596
+ # MIRRORED into group_vars (openviking_embedding_*), whose value the VM boot
597
+ # renders — on the VM route an opt-in is written as inventory host vars beside
598
+ # these keys; tests/openviking.test.ts pins the SHIPPED default to local.
599
+ embedding_provider = "local"
600
+ embedding_model = "bge-small-en-v1.5-f16"
601
+ embedding_dimension = 384
602
+ embedding_key_file = "openai_embeddings_api_key"
603
+ # The local embedder's MODEL FILE — a rule-7 pin like `version`: the image
604
+ # downloads the GGUF from `local_embedder_url` at BUILD time and refuses it
605
+ # unless its sha256 matches; the service re-checks the hash at every start.
606
+ # TRIPLE MIRROR: these == group_vars openviking_local_embedder_* == the
607
+ # memory Dockerfile's model layer (tests/openviking.test.ts,
608
+ # tests/memory-image.test.ts and the scripts/deploy.sh preflight refuse on
609
+ # drift). A changed pin needs `scripts/deploy.sh rebuild`.
610
+ local_embedder_file = "/opt/openviking/models/bge-small-en-v1.5-f16.gguf"
611
+ local_embedder_url = "https://huggingface.co/CompendiumLabs/bge-small-en-v1.5-gguf/resolve/d32f8c040ea3b516330eeb75b72bcc2d3a780ab7/bge-small-en-v1.5-f16.gguf"
612
+ local_embedder_sha256 = "f0b2fef971e8366438bfd2d9aefea1b0115919389448806d290237f638bae999"
613
+ # The service: loopback only, never published (mirrored as
614
+ # openviking_local_embedder_port / _threads). One thread is the latency budget
615
+ # the model was chosen on — ~25 ms for a round's context query.
616
+ local_embedder_port = 1934
617
+ local_embedder_threads = 1
618
+
619
+ [companion]
620
+ # The local companion container — an ambient pi session on the owner's
621
+ # PC that watches the daemon through the TUI's feed and raises alert files.
622
+ # Every knob below is commented out: the defaults in src/config.ts
623
+ # (COMPANION_DEFAULTS) are the shipped configuration; uncomment to override.
624
+ # enabled = true # `qcr tui` starts it unless --no-companion
625
+ # container_name = "qcr-companion"
626
+ # image = "qcr-companion:release" # the release image (docker/Dockerfile) in companion mode, built from the app root: qcr companion rebuild.
627
+ # # Left unset on a compose box (the project .env pins QCR_VERSION), a first start runs the
628
+ # # already-pulled ghcr.io/quickchatai/quickreview:<QCR_VERSION> instead of building, unless a
629
+ # # local build exists; a ghcr.io/quickchatai/quickreview tag set here is pulled, never built over
630
+ # pihome_volume = "companion-pihome" # its OWN pi auth — never the VM's auth.json
631
+ # session = "companion" # tmux session inside the container
632
+ # model = "openai-codex/gpt-6.1-sol:medium"
633
+ # repo_dir = "" # host review-sandbox repo → /workspace (rw);
634
+ # # empty = this sandbox root
635
+ # tracker_dir = "~/projects/your-tracker" # → /tracker (rw)
636
+ # tracker_docs_dir = "~/projects/your-tracker-docs"
637
+ # # → /tracker-docs (READ-ONLY): the tracker
638
+ # # protocol the companion follows. Outside
639
+ # # tracker_dir, so it needs its own mount.
640
+ # reviewed_repo_dir = "~/projects/your-reviewed-repo"
641
+ # # → mounted READ-ONLY into the container: the
642
+ # # reviewed repo's source, so the companion
643
+ # # can read the code a review is about. Never
644
+ # # writable — and a missing checkout just
645
+ # # drops the mount (under-equipped, never a
646
+ # # companion that refuses to start).
647
+ # evidence_dir = "~/.local/share/qcr/evidence"
648
+ # # → /evidence (READ-ONLY): finished-
649
+ # # round evidence (transcript, ledger, prompt,
650
+ # # verdict) the TUI copies down from the VM.
651
+ # # The companion reads plain files and gains
652
+ # # NO ssh and no credential. Kept indefinitely
653
+ # # and unbounded, so the default is a
654
+ # # deliberate home OUTSIDE the repo; empty
655
+ # # means <sandbox root>/evidence (gitignored).
656
+ # feed_dir = "" # → /feed; empty = state/companion/feed
657
+ # alerts_dir = "" # → /alerts; empty = state/companion/alerts
658
+ # ssh_dir = "~/.ssh" # accepted, unused: ~/.ssh is never mounted
659
+ # overdue_grace_min = 30 # watcher knobs
660
+ # stale_alert_s = 300
661
+ # awaiting_age_h = 24
662
+ # cooldown_min = 20
663
+
664
+ [sink]
665
+ # Where auto-filed defect flags (#bug reviewer-memory entries) land.
666
+ # "github" — defects filed as ISSUES on the reviewed repo. The TUI writes a
667
+ # file-issue command; the DAEMON files it through the one
668
+ # MutationGate with the [caps] day cap, title dedup and the
669
+ # [github] issue_label. In dry-run the gate writes an intent
670
+ # instead of creating anything. Recommended for new installs.
671
+ # "wayfinder" — tickets in a local filesystem wayfinder tracker on the
672
+ # operator's PC (set root/effort/parent below).
673
+ # "none" — auto-filing off (also the default when [sink] is absent).
674
+ kind = "none" # ← FILL (10): "github", "wayfinder", or "none"
675
+ # root = "" # wayfinder: tracker root; empty = [companion] tracker_dir
676
+ # effort = "" # wayfinder: effort auto-filed tickets land in (required)
677
+ # parent = "" # wayfinder: parent map id stamped into each ticket (required)
678
+ # tickets_effort = "" # TUI [2]-tab ticket projection's effort; empty = `effort`
679
+
680
+ [backfill]
681
+ # One-shot pre-daemon history import (`qcr backfill`) — read-only against
682
+ # GitHub, no gate involvement. Every knob defaults off [github] (see
683
+ # backfillDefaults in src/config.ts); uncomment only to re-run against a
684
+ # renamed label or a different identity.
685
+ # bot_login = "your-app[bot]" # reviews(author:) — with the [bot] suffix
686
+ # bot_actor = "your-app" # same identity as a timeline actor (no suffix)
687
+ # reviewed_label = "Automatic Review" # enumeration: the bot-review era marker
688
+ # trigger_label = "AI review requested" # tier-1 trigger candidate
689
+ # in_progress_label = "AI review in progress" # tier-2 (dispatch moment) fallback
690
+ # review_team_slug = "review" # team review request = tier-1 trigger
691
+ # concurrency = 2 # per-PR detail queries in flight
692
+ # max_search_pages = 10 # 100 hits/page; search caps at 1,000
693
+
694
+ [guards]
695
+ # Posting-time guards. Enforced on the single MutationGate (packages/pi-plugin/src/guards.ts).
696
+ # The sessions' post scripts (post-review.sh, approve-pr.sh, reply-thread.sh)
697
+ # hold no guard logic: they ask the daemon to post over its IPC socket
698
+ # (src/ipc/postshim.ts, src/postops.ts), so every post meets the same guards.
699
+ # All are on; a knob exists so a guard can be taken out of the
700
+ # path deliberately, in one place.
701
+ downgrade_on_human_approval = true # human APPROVED at this head ⇒ COMMENT, not REQUEST_CHANGES
702
+ skip_unchanged_head = true # same head, no author reply since ⇒ post nothing
703
+ recheck_pr_state_before_post = true # re-read the PR right before posting; merged/closed ⇒ refuse
704
+ # Guard 4: no green CI at this head ⇒ APPROVE is unavailable,
705
+ # regardless of findings. The review still posts, as a COMMENT with approval
706
+ # withheld, and the PR is re-queued when CI goes green at that head.
707
+ # Only the MOST RECENT run of each check name counts — GitHub auto-cancels
708
+ # superseded runs, and their permanent `cancelled` husks would otherwise
709
+ # withhold every approval forever. The latest run
710
+ # of every name must succeed; a genuinely failing/cancelled LATEST run still
711
+ # blocks exactly like a failing test.
712
+ block_approve_on_red_ci = true # red/pending CI ⇒ approval withheld, review posts as COMMENT
713
+ # A head with ZERO check-runs (conflicted PR, CI not
714
+ # started) carries no execution evidence — withhold there too. Set true for a repo that
715
+ # always runs CI (then `none` means something is wrong, not "no CI setup").
716
+ no_checks_is_red = true # zero checks at head ⇒ approval withheld until checks pass
717
+ # Guard 5: the head the round actually reviewed must still
718
+ # be the PR's live head at posting time. Sessions export QCR_EXPECTED_HEAD
719
+ # (the round prompt cites the sha) before posting; a mismatch refuses the
720
+ # post — the session ends the round without a verdict and the daemon re-runs
721
+ # against the new head. A missing expectation warns loudly but proceeds
722
+ # (sessions on older prompts — same belt-and-braces as rule 10's legacy input).
723
+ refuse_on_head_drift = true # live head != the sha the round reviewed ⇒ refuse to post
724
+
725
+ [recheck]
726
+ # Post-approval delta recheck. Once a PR is approved, these triggers
727
+ # ask the PR's OWN sticky session one question — "does this delta warrant a
728
+ # review, or change the verdict to REQUEST_CHANGES?" — with the standing
729
+ # verdict UPHELD BY DEFAULT. A trivial or empty delta costs one classification
730
+ # and a no-op; the only transition the path can produce is
731
+ # APPROVE -> REQUEST_CHANGES (never a re-approve, never a park). The fourth
732
+ # trigger, an author reply, is owned by ping-pong (src/pingpong.ts) and has no
733
+ # knob here.
734
+ enabled = true
735
+ on_push = true # a push landing after an approve — the core case
736
+ on_ci = true # CI completing at the approved head (adverse => evidence)
737
+ on_base_advance = true # the base branch moved; no PR-owned file changed => no-op
738
+
739
+ [review]
740
+ # A first-party agent-directive edit (.claude/**, prompts/**,
741
+ # AGENTS.md/CLAUDE.md in the reviewed repo) never auto-blocks on `kind:
742
+ # injection` — the round still decides on the merits (there is no park) and
743
+ # the review carries ONE short escalation comment
744
+ # @-mentioning the most recent human author of the touched directive files. This is the fallback mention when git history yields no
745
+ # attributable human login. It is a comment @-mention through the gated review
746
+ # — never a GitHub reviewer nomination (the never-hand-to-a-human rule).
747
+ escalation_mention_default = "" # ← FILL (8): fallback GitHub login for escalation @-mentions ("" = no @)
748
+ # The review preset: "default" (the measured best value — every
749
+ # benchmark-driven lever on, the lean plan), "max" (every model at its highest
750
+ # reasoning level + the full lever plan; ~2x cost, ~3x time) or "low" (every
751
+ # model at low reasoning; ~half default's cost and time, about the pre-lever
752
+ # review's recall). REVIEW_PRESETS in
753
+ # packages/pi-plugin/src/reviewconfig.ts. A lever set below overrides the preset.
754
+ preset = "default"
755
+ # The default preset fetches the SOURCE of the npm/PyPI packages a PR imports
756
+ # (package names and versions go to the public registries; nothing is
757
+ # installed or run) so passes can read what a library does. Set false to keep
758
+ # the reviewer off those registries — e.g. when your package names are private.
759
+ # dependency_sources = false
760
+ # Code-quality lenses (maintainability, duplication, abstraction; capped below
761
+ # blocking) run once a PR's non-test code adds 20 lines; the style lens runs
762
+ # only when your repo has a written style guide (a STYLEGUIDE/CODE_STYLE file
763
+ # or a "Code style" section in CONTRIBUTING) and applies only that guide.
764
+ # quality_lenses = false
765
+ # style_lens = false
766
+ # How many nits a review posts: "strict" (default — true-but-trivial nits are
767
+ # dropped, and review volume comes out of nits) or "open" (every true nit
768
+ # posts; style still needs your written guide).
769
+ # nits = "open"
770
+ # Per-lens cost knobs — both off by default, and the defaults are the review
771
+ # as shipped. skip_lenses lists passes never planned nor addable (known pass
772
+ # names only; pass-validate is refused — switch the validator off in the
773
+ # profile instead). validate_consequence_rule
774
+ # adds the validator's consequence rules (intent/contract appeals post as a
775
+ # [question], dead or latent code demotes to nit, a CI-enabled lint rule is a
776
+ # build defect, merges need a shared consequence).
777
+ # skip_lenses = ["pass-abstraction", "pass-duplication"]
778
+ # validate_consequence_rule = true
779
+ # Review latency levers (bench/aacr/PERF.md §6) — each removes
780
+ # serial time; none changes which lenses run. All on by default except
781
+ # validator sharding:
782
+ # wide_lens_fanout — a batched lens launch runs as wide as
783
+ # [engine] lens_max_concurrency (deprecated: the lens tool sets its own
784
+ # width);
785
+ # task_files — the daemon writes each lens's complete task to a file, and
786
+ # the engine starts the planned discovery wave from those files at
787
+ # round start and the session's `lens_wave` tool joins it
788
+ # (packages/pi-plugin/src/engine/lens/tool.ts; off ⇒ hand-written per-row `lens` calls);
789
+ # render_outputs — the review body and inline comments are rendered from
790
+ # the findings record instead of being written out a second time;
791
+ # validator_shards — EXPERIMENTAL, off (1), unvalidated: split the
792
+ # candidates over this many pass-validate children. Measured once on a
793
+ # 19-lens PR (PERF.md §7): the validator got SLOWER (101 -> 225 s) and the
794
+ # merged record dropped every doubted and downgraded mark, so it never
795
+ # earned the default; kept as a knob, not a recommended setting.
796
+ # wide_lens_fanout = false # deprecated: a no-op when [engine] runtime = "sdk" (the default)
797
+ # task_files = false
798
+ # render_outputs = false
799
+ # validator_shards = 2
800
+ # Duplicate merge (docs/duplicate-merge.md): one comment per root
801
+ # cause. Lens candidates are grouped deterministically before the validator
802
+ # (same file within 10 lines, a shared named identifier, or the same fix
803
+ # target), the validator merges or splits each group, and a post-check at
804
+ # render time merges two posted findings that are one root cause posted twice.
805
+ # dedup_merge = false
806
+ # Per-file coverage check: after the lenses return, every changed
807
+ # non-test source file no lens raised a finding on gets a targeted pass-sweep
808
+ # row (at most two, packed by line budget), each saying why it ran.
809
+ # coverage_check = false
810
+ # Follow-up rounds (docs/follow-up-rounds.md): every round after the
811
+ # first verifies the prior findings and reviews only the PR-owned change since
812
+ # the last reviewed head (base merges and rebases taken out), with one
813
+ # whole-PR pre-approval sweep before an approval when no full look has run
814
+ # since round 1 (or, once approved, when a push is large). Any doubt (no reviewed head, a rewound base, a git failure, a
815
+ # delta above follow_up_max_delta_ratio of the PR's diff lines) runs a
816
+ # full re-review instead. "full" re-reviews the whole PR every round.
817
+ # follow_up = "full"
818
+ # follow_up_max_delta_ratio = 0.6
819
+ # follow_up_sweep = false
820
+ # After a posted approval the sweep is owed only when the change since it
821
+ # reaches this many lines (additions + deletions); 0 never owes it.
822
+ # follow_up_post_approval_sweep_lines = 500
823
+ # A delta of at most this many lines gets the LEAN plan: no per-file sweep
824
+ # rows, and the quality and style lenses offered as addable instead of
825
+ # planned; 0 never. Read only under follow_up_extent = "rules"; the shipped
826
+ # "session" mode always plans the lean core and leaves this threshold to
827
+ # the session.
828
+ # follow_up_small_delta_lines = 150
829
+ # Split pass-baseline on a very large change: over this many changed
830
+ # lines (prose half; lockfiles, generated and vendored files not counted) the
831
+ # baseline is planned as 2-3 rows, each starting from one group of the
832
+ # change's files with the whole PR still its charter, so the round's longest
833
+ # reading runs in parallel. 2000 splits only the largest PRs; 0 never splits.
834
+ # baseline_split_lines = 2000
835
+ # The round's CONTEXT PACK (docs/prompt-caching.md): "pack-first"
836
+ # writes the diff, the change since the last review and the most-changed
837
+ # files' full text beside the task files, and every lens child carries it at
838
+ # the start of its prompt, after one cached warm-up per cache group — so the
839
+ # lenses read it from cache instead of re-reading it uncached (each lens's
840
+ # instructions move into a developer message after the pack). On by default
841
+ # (measured 40% cheaper on 10 cases, verdicts and recall held); "off" sends
842
+ # no pack. Needs [engine] lens_cache_group > 0.
843
+ # context_pack = "off"
844
+ # context_pack_max_tokens = 40000
845
+ # Loosened-test hint: weakened assertions in the PR's test files
846
+ # (exact → bound or contains, a tolerance widened, a count lowered, a timeout
847
+ # lengthened, an assert removed or commented out, a skip added) are found by
848
+ # code and named in pass-tests' plan reason; their presence also plans it.
849
+ # loosened_test_hint = false
850
+ # Repo profile — names a directory under profiles/ whose house-rule
851
+ # addenda pack (profiles/<name>/addenda/*.md) composes onto the built pass
852
+ # bodies after the built-in generic registry, and whose optional
853
+ # heuristics.toml extends lens selection with your repo's file shapes
854
+ # (framework settings globs, command dirs, flag-library names — ADD-only, the
855
+ # generic selection core always stays). "" = no profile (generic addenda and
856
+ # generic selection only). Ship-with options: "default" (the shipped
857
+ # behaviour — the base your own profile extends with `extends = "default"`),
858
+ # "minimal" (empty pack), "example-django" (a worked illustrative Django pack,
859
+ # heuristics included).
860
+ # See docs/profiles.md for authoring your own.
861
+ profile = ""
862
+
863
+ # EXTENSIONS to the first-party directive-path vocabulary the
864
+ # directive-edit escalation flow keys on (packages/pi-plugin/src/severity.ts). Extend-only by design:
865
+ # these ADD names, the built-ins (.claude/**, .cursor/**, root prompts/**,
866
+ # AGENTS.md/AGENT.md/CLAUDE.md/.cursorrules anywhere,
867
+ # .github/copilot-instructions.md) can never be removed — dropping one would
868
+ # silently re-enable auto-blocking on first-party directive edits. Leave
869
+ # empty unless your repo keeps agent directives under other names.
870
+ [review.directives]
871
+ extra_basenames = [] # e.g. ["COPILOT.md"]
872
+ extra_dir_segments = [] # e.g. [".aider"] — whole subtree, any depth
873
+ extra_root_dirs = [] # ROOT-only directive dirs, like prompts/
874
+ extra_files = [] # exact repo-relative paths
875
+ extra_vendored_segments = [] # extra third-party markers (case-insensitive)
876
+
877
+ # ---------------------------------------------------------------------------
878
+ # [escalation] — OPTIONAL, OFF BY DEFAULT.
879
+ #
880
+ # Two things this does NOT do, and cannot be configured to do:
881
+ #
882
+ # * It never hands the review back. The reviewer always decides on the
883
+ # merits and posts the verdict; an escalation is a note that goes out
884
+ # BESIDE it. Nothing here can withhold, defer or replace a verdict.
885
+ # * It never asks anyone to "review this PR". Every escalation names ONE
886
+ # thing — a file and line range, a migration, a named decision — and why
887
+ # that person. Think of it as a `[check this]` item with a name on it.
888
+ #
889
+ # Leave `enabled = false` and nothing below is read: no rule is evaluated, no
890
+ # extra API call is made, and the round prompts carry no escalation text
891
+ # whatever — the system BEHAVES exactly as a build without the feature.
892
+ # (The placeholder that renders empty leaves a blank line in the prompt, like
893
+ # the sibling notes beside it.)
894
+ # `qcr onboard`'s escalation step writes this section.
895
+ #
896
+ # The configuration cannot be half-done: `enabled = true` with no `targets`,
897
+ # or a rule pointing at a login that is not a target, or a rule that declares
898
+ # no condition, all REFUSE TO LOAD. That is deliberate — escalation switched
899
+ # on with nobody to escalate to is not a configuration.
900
+ [escalation]
901
+ enabled = false
902
+ # "comment" (the default) posts a gated COMMENT review @-mentioning the person
903
+ # with the narrow ask. "review_request" ALSO puts the PR in their
904
+ # review-requested queue — GitHub words that entry "review this pull request",
905
+ # which is exactly the framing this feature rules out, so the comment is still
906
+ # posted and still carries the real ask. "comment" is the shape that matches
907
+ # the intent; pick "review_request" only if your team lives in that queue.
908
+ shape = "comment"
909
+ # The CLOSED set of logins any escalation may reach. A session that names
910
+ # anyone else is silently dropped by the daemon — it is not negotiable.
911
+ targets = []
912
+ # May the PR's session ALSO escalate on its own judgement, bounded to the
913
+ # targets above? Off by default: the rules below stand on their own, and they
914
+ # are the half with no model in the loop.
915
+ session_judgement = false
916
+ # Hard cap per PR, across every round.
917
+ max_per_pr = 2
918
+
919
+ # Deterministic triggers. Each rule is a pure predicate over facts the daemon
920
+ # already has — no LLM judgement anywhere. A rule fires when EVERY condition it
921
+ # declares holds; a rule that declares none refuses to load.
922
+ #
923
+ # Conditions: paths (glob: * within a segment, ** across), labels (PR labels),
924
+ # min_severity (the round posted a finding at or above P0/P1/P2/P3), and
925
+ # failing_checks (the LATEST run of a named check is not green).
926
+ #
927
+ # [[escalation.rules]]
928
+ # name = "migrations"
929
+ # to = "example-teammate" # must appear in `targets` above
930
+ # why = "you own this repo's migration policy"
931
+ # paths = ["**/migrations/*.py"]
932
+ # [[escalation.rules]]
933
+ # name = "blockers-on-payments"
934
+ # to = "example-teammate"
935
+ # why = "payments changes go past you before they merge"
936
+ # paths = ["billing/**"]
937
+ # min_severity = "P1"
938
+
939
+ # Bounds on a SESSION-issued `drain` / `pause-pr`. A pi session
940
+ # (the overseer, or a PR's session on the owner's behalf in Slack) may hold
941
+ # either lever only for a bounded time, drawn from a per-day minute budget —
942
+ # so a session can stop review work for a reason, never indefinitely. The
943
+ # OPERATOR's own levers (`qcr drain on`, the deploy tab's D) are NOT bounded
944
+ # here: the deploy-window drain stands until the new boot's first healthy
945
+ # reconcile lifts it. Which of the two a command is comes
946
+ # from the channel it arrived on (the operator's is the root-owned
947
+ # `commands/operator/` directory a session cannot write into), never from a
948
+ # field in the file. All values are minutes; budgets count per [ops] timezone day.
949
+ [levers]
950
+ session_drain_max_min = 30 # longest single session drain
951
+ session_drain_budget_min_per_day = 90 # total session-drain minutes per day (>= the cap)
952
+ session_pause_max_min = 240 # longest single session pause of one PR
953
+ session_pause_budget_min_per_day = 480 # total session-pause minutes per PR per day (>= the cap)
954
+
955
+ # Archiving (docs/archiving.md): closed-PR history leaves every live
956
+ # path for a compressed archive under <root>/archive/ — nothing of analysis
957
+ # value is deleted, only proven duplicates. The archiver runs on its own timer,
958
+ # never before the first healthy reconcile + start_delay_min, and holds while
959
+ # draining, while the event loop is stalled or the CPU starved, and while
960
+ # either mount's free space is low or unknown. "plan" (the shipped value)
961
+ # computes what it WOULD move — status.json `archive`, `qcr status`,
962
+ # `qcr archive status --plan` — and writes nothing. "on" MOVES, every tick:
963
+ # first it restores reopened PRs' transcripts and payloads to their live paths;
964
+ # then it moves the old reader-less event rows and webhook deliveries into
965
+ # monthly SQLite files under archive/db/ (sealing finished months to .gz);
966
+ # then each eligible closed PR's files — every pi transcript, round prompt,
967
+ # session-command payload, finding, receipt, round file, inject leftover and
968
+ # processed verdict — into archive/pr-<n>/, REMOVING each from the live tree
969
+ # once its compressed copy is verified; then the overseer's non-current
970
+ # transcripts and pi's day-old global session files the same way. Nothing is
971
+ # deleted that is not verified in the archive or a proven duplicate. Read one
972
+ # `qcr archive status --plan` before flipping it. All optional — these are the
973
+ # defaults.
974
+ [archive]
975
+ mode = "plan" # off | plan | on — an unknown value refuses to load
976
+ after_days = 14 # closed PRs' files and webhook deliveries (clamped 7..3650)
977
+ events_after_days = 35 # the reader-less event kinds (clamped 31..3650; keeps qcr metrics d30 exact)
978
+ interval_s = 60 # the archiver's own timer (clamped 10..3600)
979
+ start_delay_min = 10 # after the first healthy reconcile (clamped 0..1440)
980
+ tick_budget_s = 20 # no new file is started after this (clamped 1..300)
981
+ max_prs_per_tick = 5 # (clamped 1..100)
982
+ max_mb_per_s = 8 # input rate cap for compress + verify (clamped 1..200)
983
+ disk_headroom_gb = 2 # free space kept on both mounts (clamped 0.5..1024)
984
+ db_batch_rows = 500 # the DB mover's starting batch; adapts 100..2000 to stay <= 25 ms
985
+ gzip_level = 6 # (clamped 1..9)
986
+ live_retention_days = 30 # a worker round's logs (state/live, state/lens) leave the live tree this long after
987
+ # their last write, once the PR's archive holds a verified copy (clamped 7..3650)
988
+ live_cap_gb = 20 # disk cap on state/live + state/lens; past it the archive_live_cap wake fires (0 = off, max 4096)
989
+
990
+ # The bot login's single refresher (src/loginrefresh/): the VM-only
991
+ # exception to "only the owner's pi /login refreshes" (packages/pi-plugin/src/engine/auth.ts).
992
+ # After the cutover ONE process the daemon spawns and supervises owns
993
+ # pihome/agent/auth.json and every worker borrows a refresh-blanked copy.
994
+ # Off until the cutover's config turns it on: while tmux pi sessions exist they
995
+ # refresh the file themselves, and a second refresher would rotate the refresh
996
+ # token under them. margin_min must exceed [daemon] round_deadline_min.
997
+ [login_refresh]
998
+ enabled = false
999
+ margin_min = 90 # refresh once the token has less than pi's 5 min + this left
1000
+
1001
+ # The companion's whole-repo scan — read only inside the companion,
1002
+ # never on the VM. Stage 1 runs `pass-scan` over every unit of the saved unit
1003
+ # cut (tests, migrations, lockfiles and prose never). The FIRST scan at these
1004
+ # settings is a calibration run over a few units; every later cost preview is
1005
+ # priced from the coverage ledger's actual costs, and a run stops hard at the
1006
+ # estimate the operator approved. A repo profile's `[scan]` table overrides
1007
+ # effort / tier / validator / exclude / depth / deep_lenses (the profile
1008
+ # wins); the rest is the install's. All optional — these are the defaults.
1009
+ [scan]
1010
+ effort = "low" # stage 1's reasoning level: low | medium | high (= [models] effort_max)
1011
+ tier = "sol-med" # whose model pass-scan runs on ([models.tiers])
1012
+ validator = true # the skeptic: judges each finding at its lens's tier — confirmed / likely / doubtful, never dropped (its cost is in the price)
1013
+ exclude = [] # repo-relative globs never scanned, e.g. ["generated/", "*.pb.go"]
1014
+ calibration_units = 3 # units the first-scan calibration run reads
1015
+ calibration_max_usd = 3.0 # the calibration run's hard stop
1016
+ concurrency = 8 # scan cases in flight at once (stage 1 and stage 2)
1017
+ case_max_files = 30 # one case reads at most this much; a bigger unit splits, name kept
1018
+ case_max_lines = 3000 # (stage-2 rows too: split a bigger row)
1019
+ unmeasured_rate_factor = 4 # stage 2: a deep lens never priced here costs stage 1's rate × this
1020
+ depth = "standard" # "deep": stage 1 names a deep lens for every file, every lens digs past its row's
1021
+ # files, a stage-2 proposal adds the floor below, and only a deep reading covers a file
1022
+ deep_lenses = ["pass-consistency", "pass-delivery", "pass-duplication", "pass-maintainability"]
1023
+ # the deep floor: each of these over every file stage 1 reads (one switched off by the profile is skipped);
1024
+ # the order is the priority: under a budget the first lens covers the whole tree before the next starts
1025
+ # Where `qcr scan start` drives a run (not a profile key). "docker": in a throwaway
1026
+ # container from the companion image, mounting only the reviewed repo (read-only),
1027
+ # the state home, the companion's pi login (read-only) and this config (read-only)
1028
+ # — `qcr scan doctor` checks it all, and an image older than the checkout is
1029
+ # refused until `qcr scan rebuild` (which builds it and touches no container).
1030
+ # Unset, it is "host" inside the companion container.
1031
+ # "host": a detached process on this machine, running the `pi` on the PATH (its own login).
1032
+ #
1033
+ # One login: the companion's. Log its pi in once (`qcr scan login` runs pi's OAuth
1034
+ # login straight away, in the running companion or over its pi home volume); the
1035
+ # companion alone refreshes it.
1036
+ # A scan reads that login read-only and runs every case on a fresh private copy with
1037
+ # the refresh token neutralised, so it never refreshes and never writes anything the
1038
+ # companion reads. The price: a scan case cannot outlive the access token — a start
1039
+ # or a case it would not outlive is refused; open the companion so it refreshes.
1040
+ sandbox = "docker"
1041
+ # pihome_volume = "companion-pihome" # whose login the scan reads; default [companion] pihome_volume
1042
+ # image = "qcr-scan" # the sandbox's image (docker/companion/Dockerfile): qcr scan rebuild
1043
+
1044
+ [slack]
1045
+ # The Slack concierge — ONE channel, one thread per PR. Tokens are
1046
+ # NEVER here: bot_token_file / app_token_file name flat files under
1047
+ # [github].secrets_dir (~/.config/qcr/<install>/secrets, /opt/quickreview/secrets in the
1048
+ # container). Turning `enabled` on is a deliberate deploy-time act: with it on,
1049
+ # a missing token file is a hard startup error, never a silent read-only mode.
1050
+ enabled = false
1051
+ channel = "" # ← FILL (9, if enabling Slack): the ONE channel id (C…)
1052
+ channel_kind = "public" # "public" | "private" (private needs an invite)
1053
+ bot_token_file = "slack_bot_token" # xoxb-…
1054
+ app_token_file = "slack_app_token" # xapp-…, connections:write, socket mode only
1055
+ legacy_owner_slack_id = "" # LEGACY alias of [owner] slack_id — set the [owner] key instead
1056
+ post_min_interval_ms = 1100 # Slack chat.postMessage is ~1 msg/s per channel
1057
+ reconnect_backoff_ms = [1000, 2000, 5000, 15000, 30000]
1058
+ server_ping_timeout_ms = 30000 # no server ping within this ⇒ reconnect
1059
+ catchup_max_age_s = 300 # accepted, unused: Slack inbound turns are retired
1060
+ max_reply_chars = 3000
1061
+ name_cache_ttl_s = 86400
1062
+
1063
+ [slack.users]
1064
+ # Slack member id -> GitHub login. The ONLY source of own-PR rights: a message
1065
+ # from an id that is not listed here can never claim to be a PR's author.
1066
+ # U0OPERATOR = "operator-github-login"
1067
+
1068
+ [slack.policy]
1069
+ # Every cap below is enforced in DAEMON CODE (src/slack/policy.ts), never by
1070
+ # prompt — a session can ask to speak, the daemon decides.
1071
+ daily_turns = 80 # channel-wide model turns per policy day
1072
+ per_user_daily_turns = 20 # per person, same window
1073
+ thread_replies_per_day = 6 # daemon posts into any one thread
1074
+ soft_exchange_limit = 3 # accepted, unused (no reader enforces it)
1075
+ announcements_per_hour = 20 # rolling hour; announcements bypass turn caps
1076
+ default_cooldown_min = 60 # for a non-built-in cause with no cooldown_min of its own
1077
+ # The day boundary for every cap (DST-correct). Defaults from [ops] timezone;
1078
+ # uncomment to run the Slack caps on another clock.
1079
+ #timezone = "Europe/Warsaw"
1080
+ pickup_grace_min = 20
1081
+ downtime_notice_min = 5
1082
+ heartbeat_interval_min = 5
1083
+ storm_corrections = 5 # accepted, unused: no code raises reconcile.storm
1084
+ awaiting_author_stale_h = 48 # accepted, unused: no code raises pr.awaiting_author_stale
1085
+ broadcast_verdicts = "never" # accepted, unused: verdicts always stay in-thread
1086
+
1087
+ [slack.policy.causes]
1088
+ # Per-cause switch + cooldown. Keys are per-PR at runtime ("round.overdue:5263")
1089
+ # so one stuck PR can never silence another.
1090
+ "round.overdue" = { enabled = true, cooldown_min = 60 }
1091
+ "round.malformed_verdict" = { enabled = true, cooldown_min = 60 }
1092
+ "review.failed_loud" = { enabled = true, cooldown_min = 60 }
1093
+ "pr.not_picked_up" = { enabled = true, cooldown_min = 120 }
1094
+ "daemon.up" = { enabled = true, cooldown_min = 0 }
1095
+ "daemon.stopping" = { enabled = true, cooldown_min = 0 }
1096
+ "slack.cap_reached" = { enabled = true, cooldown_min = 0 }
1097
+ "reconcile.storm" = { enabled = false, cooldown_min = 180 }
1098
+ "digest.eod" = { enabled = false, cooldown_min = 0 }
1099
+ "pr.awaiting_author_stale" = { enabled = false, cooldown_min = 1440 }