@loopover/engine 1.0.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 (272) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +661 -0
  3. package/README.md +676 -0
  4. package/dist/advisory/gate-advisory.d.ts +151 -0
  5. package/dist/advisory/gate-advisory.js +512 -0
  6. package/dist/ai-policy-map.d.ts +97 -0
  7. package/dist/ai-policy-map.js +377 -0
  8. package/dist/ams-policy-spec.d.ts +56 -0
  9. package/dist/ams-policy-spec.js +165 -0
  10. package/dist/calibration-dashboard.d.ts +23 -0
  11. package/dist/calibration-dashboard.js +65 -0
  12. package/dist/calibration-trend.d.ts +40 -0
  13. package/dist/calibration-trend.js +60 -0
  14. package/dist/contributor-fit.d.ts +24 -0
  15. package/dist/contributor-fit.js +50 -0
  16. package/dist/discovery-index-contract.d.ts +74 -0
  17. package/dist/discovery-index-contract.js +184 -0
  18. package/dist/discovery-soft-claim.d.ts +35 -0
  19. package/dist/discovery-soft-claim.js +50 -0
  20. package/dist/duplicate-winner.d.ts +57 -0
  21. package/dist/duplicate-winner.js +98 -0
  22. package/dist/feasibility.d.ts +27 -0
  23. package/dist/feasibility.js +86 -0
  24. package/dist/finding-severity-calibration.d.ts +116 -0
  25. package/dist/finding-severity-calibration.js +457 -0
  26. package/dist/fleet-run-manifest.d.ts +41 -0
  27. package/dist/fleet-run-manifest.js +151 -0
  28. package/dist/focus-manifest/guidance.d.ts +21 -0
  29. package/dist/focus-manifest/guidance.js +222 -0
  30. package/dist/focus-manifest.d.ts +826 -0
  31. package/dist/focus-manifest.js +2730 -0
  32. package/dist/gate-verdict-calibration.d.ts +111 -0
  33. package/dist/gate-verdict-calibration.js +411 -0
  34. package/dist/github/constants.d.ts +1 -0
  35. package/dist/github/constants.js +1 -0
  36. package/dist/github/sanitize-public-comment.d.ts +1 -0
  37. package/dist/github/sanitize-public-comment.js +26 -0
  38. package/dist/goal-model.d.ts +7 -0
  39. package/dist/goal-model.js +87 -0
  40. package/dist/governor/action-mode.d.ts +55 -0
  41. package/dist/governor/action-mode.js +74 -0
  42. package/dist/governor/budget-cap.d.ts +49 -0
  43. package/dist/governor/budget-cap.js +34 -0
  44. package/dist/governor/chokepoint.d.ts +58 -0
  45. package/dist/governor/chokepoint.js +269 -0
  46. package/dist/governor/kill-switch.d.ts +38 -0
  47. package/dist/governor/kill-switch.js +62 -0
  48. package/dist/governor/rate-limit.d.ts +40 -0
  49. package/dist/governor/rate-limit.js +63 -0
  50. package/dist/governor/reputation-throttle.d.ts +50 -0
  51. package/dist/governor/reputation-throttle.js +94 -0
  52. package/dist/governor/run-halt.d.ts +39 -0
  53. package/dist/governor/run-halt.js +83 -0
  54. package/dist/governor/self-plagiarism.d.ts +47 -0
  55. package/dist/governor/self-plagiarism.js +200 -0
  56. package/dist/governor/write-rate-limit.d.ts +52 -0
  57. package/dist/governor/write-rate-limit.js +132 -0
  58. package/dist/governor-ledger.d.ts +25 -0
  59. package/dist/governor-ledger.js +91 -0
  60. package/dist/index.d.ts +99 -0
  61. package/dist/index.js +120 -0
  62. package/dist/issue-plan-decomposition.d.ts +18 -0
  63. package/dist/issue-plan-decomposition.js +56 -0
  64. package/dist/issue-rag-query.d.ts +12 -0
  65. package/dist/issue-rag-query.js +35 -0
  66. package/dist/local-scorer.d.ts +34 -0
  67. package/dist/local-scorer.js +36 -0
  68. package/dist/metadata-best-min-score.d.ts +9 -0
  69. package/dist/metadata-best-min-score.js +9 -0
  70. package/dist/metadata-best-pick.d.ts +9 -0
  71. package/dist/metadata-best-pick.js +9 -0
  72. package/dist/metadata-min-score.d.ts +9 -0
  73. package/dist/metadata-min-score.js +13 -0
  74. package/dist/metadata-top-min-score.d.ts +9 -0
  75. package/dist/metadata-top-min-score.js +13 -0
  76. package/dist/metadata-top-pick.d.ts +9 -0
  77. package/dist/metadata-top-pick.js +13 -0
  78. package/dist/miner/acceptance-criteria.d.ts +43 -0
  79. package/dist/miner/acceptance-criteria.js +77 -0
  80. package/dist/miner/agent-sdk-driver.d.ts +39 -0
  81. package/dist/miner/agent-sdk-driver.js +162 -0
  82. package/dist/miner/attempt-log.d.ts +29 -0
  83. package/dist/miner/attempt-log.js +100 -0
  84. package/dist/miner/attempt-metering.d.ts +39 -0
  85. package/dist/miner/attempt-metering.js +67 -0
  86. package/dist/miner/cli-subprocess-driver.d.ts +68 -0
  87. package/dist/miner/cli-subprocess-driver.js +272 -0
  88. package/dist/miner/coding-agent-driver.d.ts +33 -0
  89. package/dist/miner/coding-agent-driver.js +36 -0
  90. package/dist/miner/coding-agent-invoke.d.ts +11 -0
  91. package/dist/miner/coding-agent-invoke.js +80 -0
  92. package/dist/miner/coding-agent-mode.d.ts +25 -0
  93. package/dist/miner/coding-agent-mode.js +43 -0
  94. package/dist/miner/driver-factory.d.ts +82 -0
  95. package/dist/miner/driver-factory.js +169 -0
  96. package/dist/miner/harness-submission-trigger.d.ts +37 -0
  97. package/dist/miner/harness-submission-trigger.js +46 -0
  98. package/dist/miner/iterate-loop.d.ts +97 -0
  99. package/dist/miner/iterate-loop.js +278 -0
  100. package/dist/miner/iterate-policy.d.ts +98 -0
  101. package/dist/miner/iterate-policy.js +98 -0
  102. package/dist/miner/lint-guard.d.ts +50 -0
  103. package/dist/miner/lint-guard.js +68 -0
  104. package/dist/miner/local-write-tools.d.ts +81 -0
  105. package/dist/miner/local-write-tools.js +113 -0
  106. package/dist/miner/loop-reentry-policy.d.ts +36 -0
  107. package/dist/miner/loop-reentry-policy.js +53 -0
  108. package/dist/miner/repo-map.d.ts +51 -0
  109. package/dist/miner/repo-map.js +268 -0
  110. package/dist/miner/self-review-adapter.d.ts +106 -0
  111. package/dist/miner/self-review-adapter.js +83 -0
  112. package/dist/miner/submission-gate.d.ts +40 -0
  113. package/dist/miner/submission-gate.js +89 -0
  114. package/dist/miner/worktree-allocator.d.ts +59 -0
  115. package/dist/miner/worktree-allocator.js +60 -0
  116. package/dist/miner/worktree-pool.d.ts +55 -0
  117. package/dist/miner/worktree-pool.js +65 -0
  118. package/dist/miner-goal-lane-fit.d.ts +18 -0
  119. package/dist/miner-goal-lane-fit.js +76 -0
  120. package/dist/miner-goal-spec.d.ts +139 -0
  121. package/dist/miner-goal-spec.js +281 -0
  122. package/dist/miner-prediction-metrics.d.ts +16 -0
  123. package/dist/miner-prediction-metrics.js +61 -0
  124. package/dist/miner-telemetry.d.ts +32 -0
  125. package/dist/miner-telemetry.js +87 -0
  126. package/dist/objective-anchor.d.ts +125 -0
  127. package/dist/objective-anchor.js +401 -0
  128. package/dist/opportunity-competition.d.ts +6 -0
  129. package/dist/opportunity-competition.js +29 -0
  130. package/dist/opportunity-freshness.d.ts +18 -0
  131. package/dist/opportunity-freshness.js +55 -0
  132. package/dist/opportunity-metadata.d.ts +53 -0
  133. package/dist/opportunity-metadata.js +204 -0
  134. package/dist/opportunity-ranker.d.ts +46 -0
  135. package/dist/opportunity-ranker.js +74 -0
  136. package/dist/pairwise-calibration.d.ts +49 -0
  137. package/dist/pairwise-calibration.js +106 -0
  138. package/dist/phase7-calibration-loop.d.ts +136 -0
  139. package/dist/phase7-calibration-loop.js +437 -0
  140. package/dist/plan-blocked.d.ts +6 -0
  141. package/dist/plan-blocked.js +24 -0
  142. package/dist/plan-completed.d.ts +5 -0
  143. package/dist/plan-completed.js +6 -0
  144. package/dist/plan-completion.d.ts +6 -0
  145. package/dist/plan-completion.js +7 -0
  146. package/dist/plan-empty.d.ts +5 -0
  147. package/dist/plan-empty.js +6 -0
  148. package/dist/plan-export.d.ts +27 -0
  149. package/dist/plan-export.js +86 -0
  150. package/dist/plan-failure.d.ts +5 -0
  151. package/dist/plan-failure.js +6 -0
  152. package/dist/plan-overall-status.d.ts +6 -0
  153. package/dist/plan-overall-status.js +25 -0
  154. package/dist/plan-pending.d.ts +5 -0
  155. package/dist/plan-pending.js +6 -0
  156. package/dist/plan-progress-complete.d.ts +7 -0
  157. package/dist/plan-progress-complete.js +10 -0
  158. package/dist/plan-ready.d.ts +6 -0
  159. package/dist/plan-ready.js +12 -0
  160. package/dist/plan-running.d.ts +5 -0
  161. package/dist/plan-running.js +6 -0
  162. package/dist/plan-skipped.d.ts +5 -0
  163. package/dist/plan-skipped.js +6 -0
  164. package/dist/plan-step-count.d.ts +5 -0
  165. package/dist/plan-step-count.js +6 -0
  166. package/dist/plan-step-stats.d.ts +5 -0
  167. package/dist/plan-step-stats.js +6 -0
  168. package/dist/plan-templates.d.ts +25 -0
  169. package/dist/plan-templates.js +107 -0
  170. package/dist/plan-terminated.d.ts +6 -0
  171. package/dist/plan-terminated.js +9 -0
  172. package/dist/portfolio/non-convergence.d.ts +37 -0
  173. package/dist/portfolio/non-convergence.js +57 -0
  174. package/dist/portfolio/queue.d.ts +30 -0
  175. package/dist/portfolio/queue.js +150 -0
  176. package/dist/predicted-gate.d.ts +103 -0
  177. package/dist/predicted-gate.js +218 -0
  178. package/dist/prompt-packet.d.ts +11 -0
  179. package/dist/prompt-packet.js +34 -0
  180. package/dist/ranked-opportunity-best-min-score.d.ts +8 -0
  181. package/dist/ranked-opportunity-best-min-score.js +9 -0
  182. package/dist/ranked-opportunity-best-pick.d.ts +8 -0
  183. package/dist/ranked-opportunity-best-pick.js +9 -0
  184. package/dist/ranked-opportunity-min-score.d.ts +8 -0
  185. package/dist/ranked-opportunity-min-score.js +13 -0
  186. package/dist/ranked-opportunity-top-min-score.d.ts +8 -0
  187. package/dist/ranked-opportunity-top-min-score.js +13 -0
  188. package/dist/review/advisory-ai-routing-config.d.ts +8 -0
  189. package/dist/review/advisory-ai-routing-config.js +40 -0
  190. package/dist/review/check-names.d.ts +1 -0
  191. package/dist/review/check-names.js +1 -0
  192. package/dist/review/cla-check.d.ts +49 -0
  193. package/dist/review/cla-check.js +71 -0
  194. package/dist/review/diff-file-priority.d.ts +1 -0
  195. package/dist/review/diff-file-priority.js +12 -0
  196. package/dist/review/enrichment-analyzer-names.d.ts +3 -0
  197. package/dist/review/enrichment-analyzer-names.js +63 -0
  198. package/dist/review/guardrail-config.d.ts +10 -0
  199. package/dist/review/guardrail-config.js +62 -0
  200. package/dist/review/linked-issue-hard-rules-config.d.ts +4 -0
  201. package/dist/review/linked-issue-hard-rules-config.js +79 -0
  202. package/dist/review/linked-issue-label-propagation.d.ts +9 -0
  203. package/dist/review/linked-issue-label-propagation.js +114 -0
  204. package/dist/review/pre-merge-checks.d.ts +28 -0
  205. package/dist/review/pre-merge-checks.js +66 -0
  206. package/dist/review/review-thread-findings.d.ts +1 -0
  207. package/dist/review/review-thread-findings.js +1 -0
  208. package/dist/review/safe-url.d.ts +5 -0
  209. package/dist/review/safe-url.js +137 -0
  210. package/dist/review/screenshot-table-gate.d.ts +87 -0
  211. package/dist/review/screenshot-table-gate.js +340 -0
  212. package/dist/review/unlinked-issue-guardrail-config.d.ts +9 -0
  213. package/dist/review/unlinked-issue-guardrail-config.js +44 -0
  214. package/dist/reviewer-consensus-calibration.d.ts +113 -0
  215. package/dist/reviewer-consensus-calibration.js +493 -0
  216. package/dist/reward-risk.d.ts +223 -0
  217. package/dist/reward-risk.js +618 -0
  218. package/dist/scoring/label-match.d.ts +4 -0
  219. package/dist/scoring/label-match.js +145 -0
  220. package/dist/scoring/model.d.ts +31 -0
  221. package/dist/scoring/model.js +157 -0
  222. package/dist/scoring/pending-pr-scenarios.d.ts +44 -0
  223. package/dist/scoring/pending-pr-scenarios.js +145 -0
  224. package/dist/scoring/preview.d.ts +246 -0
  225. package/dist/scoring/preview.js +1108 -0
  226. package/dist/scoring/types.d.ts +131 -0
  227. package/dist/scoring/types.js +7 -0
  228. package/dist/settings/auto-close-exempt.d.ts +10 -0
  229. package/dist/settings/auto-close-exempt.js +58 -0
  230. package/dist/settings/autonomy.d.ts +33 -0
  231. package/dist/settings/autonomy.js +80 -0
  232. package/dist/settings/command-authorization.d.ts +49 -0
  233. package/dist/settings/command-authorization.js +222 -0
  234. package/dist/settings/contributor-blacklist.d.ts +17 -0
  235. package/dist/settings/contributor-blacklist.js +86 -0
  236. package/dist/settings/moderation-rules.d.ts +51 -0
  237. package/dist/settings/moderation-rules.js +104 -0
  238. package/dist/settings/pr-type-label.d.ts +70 -0
  239. package/dist/settings/pr-type-label.js +149 -0
  240. package/dist/signals/change-guardrail.d.ts +49 -0
  241. package/dist/signals/change-guardrail.js +164 -0
  242. package/dist/signals/check-summary.d.ts +9 -0
  243. package/dist/signals/check-summary.js +12 -0
  244. package/dist/signals/contributor-calibration.d.ts +23 -0
  245. package/dist/signals/contributor-calibration.js +47 -0
  246. package/dist/signals/duplicate-winner.d.ts +9 -0
  247. package/dist/signals/duplicate-winner.js +9 -0
  248. package/dist/signals/path-matchers.d.ts +36 -0
  249. package/dist/signals/path-matchers.js +371 -0
  250. package/dist/signals/predicted-gate-engine.d.ts +62 -0
  251. package/dist/signals/predicted-gate-engine.js +895 -0
  252. package/dist/signals/preflight-limits.d.ts +14 -0
  253. package/dist/signals/preflight-limits.js +14 -0
  254. package/dist/signals/slop.d.ts +56 -0
  255. package/dist/signals/slop.js +324 -0
  256. package/dist/signals/test-evidence.d.ts +25 -0
  257. package/dist/signals/test-evidence.js +108 -0
  258. package/dist/subprocess-env.d.ts +23 -0
  259. package/dist/subprocess-env.js +74 -0
  260. package/dist/track-record-summary.d.ts +92 -0
  261. package/dist/track-record-summary.js +301 -0
  262. package/dist/types/manifest-deps-types.d.ts +514 -0
  263. package/dist/types/manifest-deps-types.js +4 -0
  264. package/dist/types/predicted-gate-types.d.ts +338 -0
  265. package/dist/types/predicted-gate-types.js +3 -0
  266. package/dist/types/reward-risk-types.d.ts +319 -0
  267. package/dist/types/reward-risk-types.js +11 -0
  268. package/dist/utils/json.d.ts +1 -0
  269. package/dist/utils/json.js +3 -0
  270. package/dist/version.d.ts +2 -0
  271. package/dist/version.js +3 -0
  272. package/package.json +80 -0
@@ -0,0 +1,826 @@
1
+ import type { AdvisoryAiRoutingConfig, AiReviewLowConfidenceDisposition, CombineStrategy, GatePolicyPack, GateRuleMode, JsonValue, LinkedIssueHardRulesConfig, LinkedIssueLabelPropagationConfig, OnMerge, PrTypeLabelSet, RepositorySettings, ReviewCheckMode, ScreenshotTableGateConfig, UnlinkedIssueGuardrailConfig } from "./types/manifest-deps-types.js";
2
+ import { type ReesAnalyzerName } from "./review/enrichment-analyzer-names.js";
3
+ export type FocusManifestSource = "repo_file" | "api_record" | "none";
4
+ export type FocusManifestLinkedIssuePolicy = "required" | "preferred" | "optional";
5
+ export type FocusManifestIssueDiscoveryPolicy = "encouraged" | "neutral" | "discouraged";
6
+ /**
7
+ * Maintainer-authored gate configuration declared as code in `.gittensory.yml` under `gate:`. Each
8
+ * field is `null` when the maintainer did not set it, so the resolver can layer the manifest OVER the
9
+ * DB-backed RepositorySettings (manifest > DB > safe defaults) without clobbering unset values. All
10
+ * of these flow through the SAME confirmed-contributor-gated `evaluateGateCheck` path — the manifest
11
+ * only chooses which deterministic blockers are active, never who can be blocked. There is no single
12
+ * gate master switch: each per-dimension mode (`linkedIssue`, `duplicates`, `readinessMode`, etc.)
13
+ * independently controls whether that dimension evaluates. `checkMode`/`enabled` (#2852) is a separate
14
+ * axis entirely: whether/how the "LoopOver Orb Review Agent" check-RUN publishes, independent of gate
15
+ * evaluation itself (which always runs regardless of `checkMode`/`enabled`) — see {@link ReviewCheckMode}.
16
+ */
17
+ export type FocusManifestGateConfig = {
18
+ present: boolean;
19
+ /** `gate.enabled` (legacy): a boolean shorthand for `checkMode` below -- `true` maps to `"required"`,
20
+ * `false` maps to `"disabled"` (see resolveEffectiveSettings/applyGateConfigOverrides). Despite the name,
21
+ * this controls ONLY whether the "LoopOver Orb Review Agent" check-run publishes -- it has NO effect on
22
+ * gate evaluation, disposition, comments, labels, audit, spend, or autonomous merge/close, all of which run
23
+ * identically regardless of this field. It also cannot express `checkMode`'s third state (`"visible"`), and
24
+ * is silently ignored once `checkMode` is set (see the parse-time warning a few lines below this type).
25
+ * Caused two real incidents under this exact ambiguity (2026-07: an operator flipping `enabled` to restore
26
+ * unrelated spend/gate behavior twice got a surprise check-run side effect instead) -- prefer `checkMode`
27
+ * explicitly for any new config. */
28
+ enabled: boolean | null;
29
+ /** `gate.checkMode` (#2852): explicit required|visible|disabled review-check publish mode. Takes
30
+ * precedence over the legacy `enabled` boolean above when both are set (see resolveEffectiveSettings).
31
+ * null (unset) ⇒ fall back to `enabled`, then to `settings.reviewCheckMode` (DB/dashboard), then default. */
32
+ checkMode: ReviewCheckMode | null;
33
+ pack: GatePolicyPack | null;
34
+ linkedIssue: GateRuleMode | null;
35
+ duplicates: GateRuleMode | null;
36
+ /** `gate.readiness.mode`/`gate.readiness.minScore` -- this engine-layer pair folds into
37
+ * `RepositorySettings.qualityGateMode`/`qualityGateMinScore` (src/signals/focus-manifest.ts), a third
38
+ * spelling of the same concept alongside the yml key name; the `readiness` gate is distinct from the
39
+ * separate `mergeReadiness` composite gate above. */
40
+ readinessMode: GateRuleMode | null;
41
+ readinessMinScore: number | null;
42
+ slopMode: GateRuleMode | null;
43
+ slopMinScore: number | null;
44
+ slopAiAdvisory: boolean | null;
45
+ sizeMode: GateRuleMode | null;
46
+ /** `gate.lockfileIntegrity` (#2563): off|advisory|block, off by default. When not off, a changed
47
+ * `package-lock.json` diff is scanned for a `resolved`/`integrity` change unaccompanied by a matching
48
+ * `package.json` version bump, or a `resolved` URL outside `registry.npmjs.org` — a `lockfile_tamper_risk`
49
+ * finding (`block` additionally hard-blocks). Config-as-code only — no DB column or dashboard toggle. */
50
+ lockfileIntegrityMode: GateRuleMode | null;
51
+ aiReviewMode: GateRuleMode | null;
52
+ aiReviewByok: boolean | null;
53
+ aiReviewProvider: "anthropic" | "openai" | null;
54
+ aiReviewModel: string | null;
55
+ aiReviewAllAuthors: boolean | null;
56
+ /** `gate.aiReview.closeConfidence` (#7): minimum calibrated AI-reviewer confidence (0-1) for an AI defect to BLOCK
57
+ * under `aiReview.mode: block`. null (unset) ⇒ the gate's 0.93 default. Clamped to [0,1] at parse time. */
58
+ aiReviewCloseConfidence: number | null;
59
+ /** `gate.aiReview.lowConfidenceDisposition` (#4603): disposition for a sub-`closeConfidence`-floor
60
+ * `ai_consensus_defect`/`ai_review_split` finding. null (unset) ⇒ `hold_for_review` (the shipped default).
61
+ * DB-backed (dashboard-settable too, via the `/ai-review` route); this overrides the stored value -- mirrors
62
+ * `aiReviewMode` above, not the config-as-code-only `closeConfidence` sibling field just above. */
63
+ aiReviewLowConfidenceDisposition: AiReviewLowConfidenceDisposition | null;
64
+ /** `gate.aiReview.combine` (#2567): per-repo override of the self-host operator's `AI_REVIEW_PLAN.combine`
65
+ * boot default (single/consensus/synthesis). null (unset) ⇒ the operator's plan (or `consensus`). A
66
+ * REFINEMENT only — see {@link aiReviewOnMerge} for the operator-floor clamp `runGittensoryAiReview` applies
67
+ * to the paired `onMerge` field; `combine` itself is not floor-clamped (the three strategies are not ordered
68
+ * by strictness, so there is no single "loosening" direction to clamp). */
69
+ aiReviewCombine: CombineStrategy | null;
70
+ /** `gate.aiReview.onMerge` (#2567): per-repo override of the `synthesis` merge rule. `either` is the STRICTER
71
+ * rule (any one reviewer's blocker blocks/holds); `both` is more PERMISSIVE (requires every reviewer to
72
+ * agree). null (unset) ⇒ the operator's `AI_REVIEW_PLAN.onMerge`. A repo may only TIGHTEN the operator's
73
+ * floor (never loosen `either` down to `both`) — `runGittensoryAiReview` enforces the clamp at resolve time,
74
+ * since only it can see both the per-repo value and the operator's plan. */
75
+ aiReviewOnMerge: OnMerge | null;
76
+ /** `gate.aiReview.reviewers` (#2567): per-repo override of the named reviewer pair(s) to run, in place of the
77
+ * operator's `AI_REVIEW_PLAN.reviewers` (or the free Workers-AI pair when the operator configured none). null
78
+ * (unset) ⇒ the operator's plan. No operator floor applies to WHICH reviewers run (only `onMerge` gates
79
+ * strictness), so this always wins unclamped when set. */
80
+ aiReviewReviewers: ReadonlyArray<{
81
+ model: string;
82
+ fallback?: string | null | undefined;
83
+ }> | null;
84
+ mergeReadiness: GateRuleMode | null;
85
+ manifestPolicy: GateRuleMode | null;
86
+ selfAuthoredLinkedIssue: GateRuleMode | null;
87
+ /** `gate.linkedIssueSatisfaction` (#1961/#3906): off|advisory|block, off by default. When not off, an AI
88
+ * assessment of whether the PR's diff satisfies its primary linked issue's intent runs and renders as a
89
+ * collapsible section in the review comment; `block` additionally lets a confidence-floor-passing
90
+ * "unaddressed" verdict become a hard blocker. DB-backed (dashboard-settable too); this overrides the
91
+ * stored value -- mirrors `aiReviewMode` above, not the config-as-code-only `unlinkedIssueGuardrail`
92
+ * pattern. Distinct from the pre-existing, config-as-code-only `review.linkedIssueSatisfaction` (#2173,
93
+ * below) in type and storage -- but as of #4149 (`src/signals/focus-manifest.ts`'s
94
+ * `resolveEffectiveSettings`, ~line 611-618) `review.linkedIssueSatisfaction` IS folded in as a
95
+ * fallback alias whenever this `gate:` field is unset, so setting either spelling drives the same real
96
+ * merge/close decision. See `src/types.ts`'s `linkedIssueSatisfactionGateMode` doc for the authoritative
97
+ * cross-reference. */
98
+ linkedIssueSatisfaction: GateRuleMode | null;
99
+ dryRun: boolean | null;
100
+ firstTimeContributorGrace: boolean | null;
101
+ /** `gate.premergeContentRecheck` (#2550): for a PR touching `migrations/**`, re-verify against a live,
102
+ * freshly-fetched tip of the base branch — unioned with this PR's own new migration filenames — for a
103
+ * migration-number collision immediately before an agent-driven merge, not just at CI time against the
104
+ * PR's own stale branch snapshot. On a live collision, the merge is suppressed and the PR is held with a
105
+ * rebase-needed comment instead of merging blind. null (unset) ⇒ off (byte-identical to today) — this
106
+ * costs one extra, uncached GitHub Trees-API call for any PR that touches migrations/**, so it is opt-in
107
+ * rather than a new default. */
108
+ premergeContentRecheck: boolean | null;
109
+ /** `gate.requireFreshRebaseWindow` (#2552, anti-race): minutes. When the base branch has advanced within
110
+ * this window of the actual merge-decision moment, an agent-driven merge forces an `update_branch` +
111
+ * fresh CI recheck cycle before merging, instead of trusting a `mergeableState: clean` read that may
112
+ * already be stale relative to a sibling commit that just landed on the base. null (unset) ⇒ never force
113
+ * (byte-identical to today) — a discrete positive-minutes count, not a score, so it is neither clamped
114
+ * nor rounded; an invalid value (fractional, non-positive, non-finite) is dropped with a warning. */
115
+ requireFreshRebaseWindowMinutes: number | null;
116
+ /** `gate.claMode` (#2564): off/advisory/block. null (unset) ⇒ off (byte-identical to today) — a repo must
117
+ * explicitly opt in before any CLA consent check runs. */
118
+ claMode: GateRuleMode | null;
119
+ /** `gate.cla.consentPhrase` (#2564): the required PR-body consent phrase. null (unset) ⇒ phrase-match
120
+ * detection is not configured. */
121
+ claConsentPhrase: string | null;
122
+ /** `gate.cla.checkRunName` (#2564): the CLA-bot check-run name to trust. null (unset) ⇒ check-run
123
+ * detection is not configured. */
124
+ claCheckRunName: string | null;
125
+ /** `gate.cla.checkRunAppSlug`: the trusted GitHub App slug that must produce `checkRunName`. null (unset) ⇒
126
+ * check-run detection remains unresolved rather than trusting a spoofable name-only match. */
127
+ claCheckRunAppSlug: string | null;
128
+ /** `gate.expectedCiContexts` (#selfhost-ci-verification): CI check/status context names to treat as
129
+ * required when GitHub branch-protection required-status-checks are unreadable or unconfigured. null
130
+ * (unset) ⇒ no generic fallback configured — the live-CI aggregate keeps today's fold-all behavior
131
+ * when branch protection is also unreadable. See {@link RepositorySettings.expectedCiContexts}. */
132
+ expectedCiContexts: ReadonlyArray<string> | null;
133
+ /** `gate.aiJudgmentBlockers` (#3907): "gate" | "advisory", null (unset) ⇒ "advisory" (byte-identical to
134
+ * today everywhere that doesn't opt in). Config-as-code only, YML-only (no DB column, no dashboard
135
+ * toggle) — mirrors `contentLane`'s own YML-only shape, since this only has an effect for repos already
136
+ * running the registry content lane. When "gate", a confident AI-judgment-only finding that
137
+ * applySurfaceGate's default "advisory" behavior would otherwise let a decisive surface merge override
138
+ * instead SURVIVES into the deterministic gate's own blockers array, demoting `decision` away from
139
+ * `merge` — see content-lane-wire.ts's `applySurfaceGate` guard #3 and `evaluateWithSurfaceLane` for the
140
+ * wiring. This deliberately reopens exactly the risk #2592 accepted for the general case (an AI
141
+ * hallucination can one-shot-close a structurally-clean PR) as an explicit, per-repo, documented
142
+ * trade-off — never the default. */
143
+ aiJudgmentBlockersMode: "gate" | "advisory" | null;
144
+ /** `gate.copycat.mode` (#1969): off|warn|label|block, off by default. Config-as-code only -- no DB column
145
+ * or dashboard toggle. Deliberately a DEDICATED 4-value enum, not the shared `GateRuleMode` tri-state: the
146
+ * issue's tiered response is warn -> label -> block -> strikes, where "strikes" is a separate escalation
147
+ * action (reusing the existing cross-repo banned-contributors ledger once wired) rather than a 5th mode
148
+ * value. THIS FIELD IS CURRENTLY INERT -- the similarity/containment detection engine that would actually
149
+ * compute a copycat finding does not exist yet (tracked as later, separate PRs against #1969); parsing and
150
+ * threading this config end-to-end first proves the plumbing and lets an operator's `.gittensory.yml`
151
+ * already declare intent without waiting on the detection engine. */
152
+ copycatMode: CopycatGateMode | null;
153
+ /** `gate.copycat.minScore` (#1969): containment/similarity score (0-100) at/above which `copycatMode` acts.
154
+ * null (unset) ⇒ the (also currently inert) engine's own default threshold once it exists. Same 0-100
155
+ * clamp-and-round normalization as `slopMinScore`/`readinessMinScore` above. */
156
+ copycatMinScore: number | null;
157
+ };
158
+ /** `gate.copycat.mode` (#1969) -- see {@link FocusManifestGateConfig.copycatMode}'s doc comment for why this
159
+ * is a dedicated enum rather than the shared `GateRuleMode`. */
160
+ export type CopycatGateMode = "off" | "warn" | "label" | "block";
161
+ export declare const CONVERGED_FEATURE_KEYS: readonly ["rag", "reputation", "unifiedComment", "safety", "grounding", "e2eTests", "screenshots", "improvementSignal"];
162
+ export type ConvergedFeatureKey = (typeof CONVERGED_FEATURE_KEYS)[number];
163
+ /** Per-repo activation overrides for the converged review features (`features:` block). `true`/`false` force the
164
+ * feature on/off for THIS repo (subject to the env kill-switch); `null` (unset) ⇒ the resolver falls back to the
165
+ * `GITTENSORY_REVIEW_REPOS` allowlist default, so an operator who sets nothing keeps today's behavior. */
166
+ export type FocusManifestFeaturesConfig = {
167
+ present: boolean;
168
+ } & Record<ConvergedFeatureKey, boolean | null>;
169
+ /** Optional ecosystem/network integrations under the `experimental:` block — plugins that couple gittensory to
170
+ * an external system rather than core review behavior. Starts with `gittensor` (the subnet mining-registry/
171
+ * scoring integration gittensory originally shipped with); future plugins land in this same array as the
172
+ * product broadens beyond gittensor. Deliberately a SEPARATE block from `features:` (converged review
173
+ * capabilities) — an operator (especially self-host) should be able to see at a glance which toggles are "how
174
+ * gittensory reviews PRs" vs "which external network/ecosystem this instance opts into." */
175
+ export declare const EXPERIMENTAL_PLUGIN_KEYS: readonly ["gittensor"];
176
+ export type ExperimentalPluginKey = (typeof EXPERIMENTAL_PLUGIN_KEYS)[number];
177
+ /** Per-repo activation for `experimental:` plugins. Unlike `features:`, there is no `GITTENSORY_REVIEW_REPOS`
178
+ * allowlist fallback for ANY key here — every plugin is the "manifestOnly" precedence shape
179
+ * (`resolveManifestOnlyFeature`): OFF unless the operator's global env kill-switch AND an explicit per-repo
180
+ * `true` are BOTH set. So an instance that never opts in has zero footprint from any experimental plugin. */
181
+ export type FocusManifestExperimentalConfig = {
182
+ present: boolean;
183
+ } & Record<ExperimentalPluginKey, boolean | null>;
184
+ /**
185
+ * Per-repo registry-review lane configuration (`contentLane:` block, #2435) — lets a self-hosted maintainer
186
+ * configure their OWN registry (structural file-scope patterns + entry-count cap + dedup fields) without a
187
+ * gittensory code change. `entryFileGlob` and `collectionField` are the two REQUIRED fields to build a usable
188
+ * spec; `present` is true only when both are set (a partial config degrades to "not configured," not a broken
189
+ * half-spec — see `parseContentLaneConfig`). `validatorId` optionally references a code-registered domain
190
+ * validator (`review/content-lane/spec-resolver.ts`'s `REGISTRY_VALIDATORS`); omitted ⇒ structural gating only
191
+ * (scope/count/dedup), no domain-specific semantic check — see `RegistryLaneSpec.assessAppendedEntry`.
192
+ */
193
+ export type FocusManifestContentLaneConfig = {
194
+ present: boolean;
195
+ entryFileGlob: string | null;
196
+ providerFileGlob: string | null;
197
+ artifactGlob: string | null;
198
+ collectionField: string | null;
199
+ maxAppendedEntries: number | null;
200
+ duplicateKeyFields: string[];
201
+ validatorId: string | null;
202
+ };
203
+ /** Which generated-file types the repo-doc generation roadmap (#2993) is allowed to touch for a repo.
204
+ * "agents" covers AGENTS.md/CLAUDE.md (#3000/#3004); "skills" covers generated Claude Code/Codex skill
205
+ * files once that generator lands (#3001) -- listed here now so a maintainer can opt in ahead of time. */
206
+ export type FocusManifestRepoDocGenerationScope = "agents" | "skills";
207
+ /**
208
+ * Per-repo opt-in for the repo-doc generation roadmap (#2993/#3002), declared as code under
209
+ * `repoDocGeneration:`. Purely a `.gittensory.yml` surface -- there is no DB-backed dashboard counterpart,
210
+ * so precedence is simply "the manifest value, or the default below when unset" (no DB layer to overlay).
211
+ * Defaults to fully disabled: a repo with no `repoDocGeneration:` block, or an explicit `enabled: false`,
212
+ * is never touched by the generator. `allowOverwriteExisting` is a SEPARATE opt-in specifically for a repo
213
+ * that already has a hand-maintained AGENTS.md/CLAUDE.md (no recognizable generated-content marker block,
214
+ * per generated-doc-refresh.ts's `manual-review-required` outcome) -- without it, that repo is left alone
215
+ * rather than proposed for a wholesale overwrite, even when `enabled` is true.
216
+ */
217
+ export type FocusManifestRepoDocGenerationConfig = {
218
+ present: boolean;
219
+ enabled: boolean;
220
+ scope: FocusManifestRepoDocGenerationScope[];
221
+ allowOverwriteExisting: boolean;
222
+ /** How many days must elapse between scheduled refresh attempts for this repo (#3003). Default 7 (weekly).
223
+ * Purely a rate-limiting knob on the SCHEDULED sweep -- it never affects correctness, since
224
+ * openRepoDocPullRequest's own no-change short-circuit already prevents a redundant PR regardless of how
225
+ * often it's invoked; this just avoids re-checking a stable repo more often than the operator wants. */
226
+ refreshIntervalDays: number;
227
+ };
228
+ /**
229
+ * Per-repo opt-in for the periodic maintainer review-recap digest (#1963), declared as code under
230
+ * `reviewRecap:`. Mirrors `repoDocGeneration:` exactly: no DB-backed dashboard counterpart, so the parsed
231
+ * value (or the default below when unset) IS the effective value — there is no DB layer to overlay onto.
232
+ * Defaults to fully disabled: a repo with no `reviewRecap:` block, or an explicit `enabled: false`, never
233
+ * gets a recap posted. Discord delivery ONLY for now (reuses the SAME per-repo webhook resolution as the
234
+ * per-event notifier in notify-discord.ts, `resolveDiscordWebhook`) — Slack is a follow-up.
235
+ */
236
+ export type FocusManifestReviewRecapConfig = {
237
+ present: boolean;
238
+ enabled: boolean;
239
+ /** How many days of review activity each recap covers, and (once the scheduler follow-up lands) how often
240
+ * it is posted. Default 7 (weekly). A purely descriptive/rate-limiting knob today — this PR ships only
241
+ * the manually-triggerable builder + delivery, so `cadenceDays` currently just sets the report WINDOW;
242
+ * the scheduled cron trigger is a scoped follow-up (see the PR description). */
243
+ cadenceDays: number;
244
+ };
245
+ /**
246
+ * Config-as-code override for the CROSS-repo maintainer recap digest's cron knobs (#1963, #2250), declared
247
+ * under `maintainerRecap:`. Distinct from `reviewRecap:` above (that is the single-repo digest's own window/
248
+ * enable knob); this instead overrides the GITTENSORY_MAINTAINER_RECAP / GITTENSORY_RECAP_CADENCE env vars
249
+ * that gate the cron-scheduled cross-repo digest (buildMaintainerRecap, #2239 / #2248) — read from the
250
+ * gittensory self-repo's manifest (resolveGittensorySelfRepoFullName), since the digest is an operator-level
251
+ * setting, not a per-contributor-repo one. Mirrors `reviewRecap:` exactly: no DB-backed counterpart, so the
252
+ * parsed value (or the default below when unset) IS the effective value. Not present (or present with no
253
+ * fields set) ⇒ the caller falls back to the env vars, byte-identical to before this override existed.
254
+ */
255
+ export type FocusManifestMaintainerRecapConfig = {
256
+ present: boolean;
257
+ enabled: boolean;
258
+ cadence: "daily" | "weekly";
259
+ /** Delivery channel for the digest. Discord-only for now (mirrors deliverRecapToDiscord, #2245) — Slack
260
+ * delivery for this cross-repo digest is a follow-up, so any other value falls back to "discord". */
261
+ channel: "discord";
262
+ };
263
+ /**
264
+ * Generic repository-settings override declared in `.gittensory.yml` under `settings:`. A partial of
265
+ * {@link RepositorySettings} — every behaviour a maintainer can toggle in the dashboard can be set here
266
+ * as code. Unset fields are omitted so the resolver layers it OVER the DB-backed settings
267
+ * (`.gittensory.yml` > dashboard settings > safe defaults). The friendly `gate:` block is a typed alias
268
+ * for the gate-related subset and wins over `settings:` for those fields.
269
+ */
270
+ export type FocusManifestSettings = Partial<Pick<RepositorySettings, "commentMode" | "publicAudienceMode" | "publicSignalLevel" | "checkRunMode" | "checkRunDetailLevel" | "regateSweepOrderMode" | "reviewCheckMode" | "autoProjectMilestoneMatch" | "autoProjectMilestoneMatchBackend" | "linkedIssueGateMode" | "duplicatePrGateMode" | "selfAuthoredLinkedIssueGateMode" | "qualityGateMode" | "qualityGateMinScore" | "aiReviewMode" | "aiReviewByok" | "aiReviewProvider" | "aiReviewModel" | "aiReviewAllAuthors" | "closeOwnerAuthors" | "autoLabelEnabled" | "typeLabelsEnabled" | "badgeEnabled" | "publicQualityMetrics" | "gittensorLabel" | "createMissingLabel" | "publicSurface" | "includeMaintainerAuthors" | "requireLinkedIssue" | "backfillEnabled" | "autonomy" | "autoMaintain" | "agentPaused" | "agentDryRun" | "agentGlobalFreezeOverride" | "commandAuthorization" | "contributorBlacklist" | "blacklistLabel" | "contributorOpenPrCap" | "contributorOpenIssueCap" | "contributorCapLabel" | "contributorCapCancelCi" | "reviewNagPolicy" | "reviewNagMaxPings" | "reviewNagCooldownDays" | "reviewNagLabel" | "reviewNagMonitoredMentions" | "autoCloseExemptLogins" | "hardGuardrailGlobs" | "manualReviewLabel" | "readyToMergeLabel" | "changesRequestedLabel" | "migrationCollisionLabel" | "pendingClosureLabel" | "accountAgeThresholdDays" | "newAccountLabel" | "commandRateLimitPolicy" | "commandRateLimitMaxPerWindow" | "commandRateLimitAiMaxPerWindow" | "commandRateLimitWindowHours" | "moderationGateMode" | "moderationRules" | "moderationWarningLabel" | "moderationBannedLabel" | "reviewEvasionProtection" | "reviewEvasionLabel" | "reviewEvasionComment" | "mergeTrainMode">> & {
271
+ typeLabels?: Partial<PrTypeLabelSet> | null | undefined;
272
+ linkedIssueLabelPropagation?: Partial<LinkedIssueLabelPropagationConfig> | undefined;
273
+ linkedIssueHardRules?: Partial<LinkedIssueHardRulesConfig> | undefined;
274
+ unlinkedIssueGuardrail?: Partial<UnlinkedIssueGuardrailConfig> | undefined;
275
+ screenshotTableGate?: Partial<ScreenshotTableGateConfig> | undefined;
276
+ advisoryAiRouting?: Partial<AdvisoryAiRoutingConfig> | undefined;
277
+ };
278
+ /** Field keys for the public review-panel rows a maintainer can show/hide via `review.fields`. `improvementSignal`
279
+ * (#4744) is the newest: the optional row combining the deterministic structural-improvement tier (#4742) and,
280
+ * where also active, the LLM tier's composed value judgment (#4743) -- default-shown, like every sibling here,
281
+ * when the `improvementSignal` converged feature itself is active for the repo. */
282
+ export declare const REVIEW_FIELD_KEYS: readonly ["linkedIssue", "relatedWork", "reviewLoad", "validationEvidence", "openPrQueue", "contributorContext", "gateResult", "improvementSignal"];
283
+ export type ReviewFieldKey = (typeof REVIEW_FIELD_KEYS)[number];
284
+ export declare const REVIEW_PROFILES: readonly ["chill", "balanced", "assertive"];
285
+ export type ReviewProfile = (typeof REVIEW_PROFILES)[number];
286
+ export type ReviewFindingSeverity = "critical" | "major" | "minor" | "nitpick";
287
+ export declare const REVIEW_FINDING_SEVERITY_LADDER: readonly ["critical", "major", "minor", "nitpick"];
288
+ /**
289
+ * Maintainer overrides for the public review-panel CONTENT, declared under `review:`. Customizes the
290
+ * panel without changing what gittensory measures: a custom public-safe footer lead line, a custom intro
291
+ * note, and per-row show/hide toggles. The Gittensor attribution + register link is ALWAYS appended to
292
+ * the footer regardless (the growth surface is preserved); maintainer text that fails the public-safe
293
+ * filter is dropped, never published.
294
+ */
295
+ export type FocusManifestReviewConfig = {
296
+ present: boolean;
297
+ footerText: string | null;
298
+ note: string | null;
299
+ fields: Partial<Record<ReviewFieldKey, boolean>>;
300
+ /** `review.enrichment`: per-repo REES enrichment-analyzer toggles (analyzer name → on/off). Only known analyzer
301
+ * keys are kept (unknown keys warn + drop at parse). Empty (default, absent) ⇒ the operator's default analyzer
302
+ * set runs unchanged (byte-identical). (#2050) */
303
+ enrichmentAnalyzers: Partial<Record<ReesAnalyzerName, boolean>>;
304
+ /** `review.profile`: chill / balanced / assertive. null (absent) = balanced = byte-identical reviewer prompt. */
305
+ profile: ReviewProfile | null;
306
+ /** `review.tone`: a bounded public-safe voice brief complementing `review.profile` (e.g. "concise, cite line numbers").
307
+ * Folded into the review-instructions slot at runtime. null (default, absent) ⇒ byte-identical prompt. (#2044) */
308
+ tone: string | null;
309
+ /** `review.security_focus`: when true, the AI reviewer is told to prioritize a security-defect category
310
+ * (injection, authn/authz bypass, secret handling, unsafe deserialization, SSRF, path traversal) with
311
+ * elevated scrutiny, ON TOP OF whatever `profile` volume is set — an orthogonal "what to prioritize" axis,
312
+ * not a fourth profile level. null/false (default, absent) = byte-identical reviewer prompt. (#review-security-focus) */
313
+ securityFocus: boolean | null;
314
+ /** `review.inline_comments`: when true, the AI reviewer ALSO leaves quiet, non-blocking inline PR comments on
315
+ * specific changed lines (in addition to the decision summary). null/false (default, absent) = no inline
316
+ * comments = byte-identical behavior. Operator-gated too (GITTENSORY_REVIEW_INLINE_COMMENTS + allowlist).
317
+ * (#inline-comments) */
318
+ inlineComments: boolean | null;
319
+ /** `review.fixHandoff`: when true, the reviewer emits fix-handoff blocks (copy-paste remediation guidance). null/
320
+ * false (default, absent) = no fix-handoff blocks = byte-identical. Operator-gated too (GITTENSORY_REVIEW_FIX_HANDOFF
321
+ * + the convergence cutover allowlist) — the manifest toggle is only one of the ANDed gates. (#2176, for #1962) */
322
+ fixHandoff: boolean | null;
323
+ /** `review.auto_merge_summary`: when true, the unified comment gains a READ-ONLY collapsible showing which
324
+ * auto-merge conditions currently pass/fail (CI green, gate passing, mergeable-clean, valid linked issue),
325
+ * rendered from already-computed readiness signals. SURFACE ONLY — never changes the merge/close decision.
326
+ * null/false (default, absent) = no summary = byte-identical. (#2051, for #1959) */
327
+ autoMergeSummary: boolean | null;
328
+ /** `review.suggestions`: when true, an inline finding whose AI-provided fix is precise enough to anchor to a
329
+ * single line is ALSO rendered as a GitHub-native ` ```suggestion ` block a contributor can commit in one
330
+ * click. Only takes effect when inline comments are already on (a suggestion has nothing to attach to
331
+ * otherwise) — this is an ADDITIONAL opt-in on top of `review.inline_comments`, not a replacement gate.
332
+ * null/false (default, absent) = no suggestion blocks = byte-identical behavior. (#1956) */
333
+ suggestions: boolean | null;
334
+ /** `review.changed_files_summary`: when true, the unified review comment (only rendered at all when the
335
+ * `unifiedComment` convergence feature is on) gains a deterministic, no-AI "Changed files" collapsible: one
336
+ * row per file category (source/test/docs/config/generated), with file counts and +/- totals, via the
337
+ * existing `classifyChangedFile` classifier (`src/review/changed-files-classify.ts`, built for this table
338
+ * under #2143). null/false (default, absent) = no changed-files section = byte-identical behavior. (#1957) */
339
+ changedFilesSummary: boolean | null;
340
+ /** `review.effort_score`: when true, the unified review comment (only rendered when the `unifiedComment`
341
+ * convergence feature is on) gains a compact "review effort: N/5 (~M min)" chip — a deterministic, no-AI
342
+ * complexity/time estimate from `estimateReviewEffort` (`src/review/review-effort.ts`), weighting each
343
+ * changed file's added lines by its category (source costs most; generated/vendored/lockfiles cost least)
344
+ * plus a fixed per-file overhead. Mirrors `changedFilesSummary` exactly: same table, same deterministic
345
+ * source, same display-only (never touches the AI prompt) shape. null/false (default, absent) = no chip =
346
+ * byte-identical behavior. (#1955) */
347
+ effortScore: boolean | null;
348
+ /** `review.impact_map` (#2184, config slice of #1971): when true, gates BOTH the deterministic impact-map
349
+ * computation (`computeImpactMap`, `src/review/impact-map.ts`) and its rendering as a compact section in
350
+ * the unified review comment (#2185) / additive AI-review grounding context (#2186). Deterministic/display
351
+ * + reference-context only — never touches the gate verdict. ALSO requires the global env kill-switch
352
+ * (`isImpactMapEnabled`, mirroring `isRagEnabled` in `src/review/rag-wire.ts:27`) to be on; the manifest
353
+ * flag alone cannot enable it for a self-host operator who hasn't opted in globally. null/false (default,
354
+ * absent) ⇒ no impact-map computation at all = byte-identical behavior. (#2184) */
355
+ impactMap: boolean | null;
356
+ /** `review.culture_profile` (#2995): when true, the AI reviewer's USER prompt gains an ADDITIVE "REPO
357
+ * QUALITY-CULTURE PROFILE" reference block — typical merged-PR size + common accepted labels, derived
358
+ * deterministically from this repo's OWN `recent_merged_pull_requests` history (see
359
+ * `src/review/repo-culture-profile.ts` / `repo-culture-profile-wire.ts`). Reference-only grounding, exactly
360
+ * like RAG/CI-grounding context: it never becomes a gate/scoring input and never changes the structured
361
+ * output contract. Also requires the global `GITTENSORY_REVIEW_CULTURE_PROFILE` kill-switch to be on (this
362
+ * field only opts THIS repo in once the capability itself is enabled). null/false (default, absent) = no
363
+ * section appended = byte-identical behavior. */
364
+ cultureProfile: boolean | null;
365
+ /** `review.selftune` (#4104): explicit per-repo FORCE-OFF for the self-improvement/auto-tune cron pass
366
+ * (`runSelfTune`, `src/review/selftune-wire.ts`) — `false` excludes this repo from tuning even though it's
367
+ * otherwise agent-configured (`isAgentConfigured`) and the global `GITTENSORY_REVIEW_SELFTUNE` kill-switch is
368
+ * on. Deliberately FORCE-OFF-ONLY (no `true` override): forcing a NON-agent-configured repo INTO tuning would
369
+ * bypass that separate, broader acting-autonomy consent boundary, which this key must not touch. Unlike
370
+ * `impactMap`/`cultureProfile` above, there is no `GITTENSORY_REVIEW_REPOS` allowlist fallback for selftune —
371
+ * its own scoping is `isAgentConfigured`, not the cutover allowlist — so this does NOT live under the generic
372
+ * `features:` block/`resolveConvergedFeature` (see `CONVERGED_FEATURE_KEYS`'s own comment). null/true
373
+ * (default, absent) ⇒ no change to today's agent-configured-repos-only behavior. */
374
+ selftune: boolean | null;
375
+ /** `review.memory` (#2179, config slice of #1964): when true, gates repeat-false-positive SUPPRESSION —
376
+ * before an advisory (non-blocking) AI finding is surfaced in the unified review comment, it is matched
377
+ * against this repo's stored `review_suppression` signals (a maintainer's own past false-positive
378
+ * dismissals, `src/db/repositories.ts`'s `listReviewSuppressions`, migrations/0114) and demoted/dropped on a
379
+ * match (`src/review/review-memory-match.ts`'s `matchSuppressions`). ADVISORY-ONLY BY CONSTRUCTION: it is
380
+ * never applied to gate blockers, so it can never change the merge/close disposition — only which
381
+ * non-blocking nits render. ALSO requires the global env kill-switch (`isReviewMemoryEnabled`, mirroring
382
+ * `isImpactMapEnabled` in `src/review/impact-map-wire.ts`) to be on; the manifest flag alone cannot enable
383
+ * it for a self-host operator who hasn't opted in globally. Fail-safe: a suppression-store read error or
384
+ * matcher throw leaves findings untouched. null/false (default, absent) ⇒ no suppression lookup at all =
385
+ * byte-identical behavior. */
386
+ reviewMemory: boolean | null;
387
+ /** `review.finding_categories`: when true, an inline finding is ALSO tagged with a category (security/
388
+ * correctness/performance/maintainability/tests/style) — the AI reviewer is asked to self-categorize, with a
389
+ * deterministic path/keyword fallback (`classifyFindingCategory`) covering whatever it omits. Only takes
390
+ * effect when inline comments are already on (a category has nothing to categorize otherwise) — this is an
391
+ * ADDITIONAL opt-in on top of `review.inline_comments`, not a replacement gate, mirroring `review.suggestions`.
392
+ * null/false (default, absent) = no category tagging = byte-identical behavior. (#1958) */
393
+ findingCategories: boolean | null;
394
+ /** `review.inline_comments_per_category`: optional per-category sub-cap applied before the total inline-comment
395
+ * cap so one category (e.g. style) cannot crowd out security/correctness findings. null (default, absent) ⇒
396
+ * byte-identical first-seen selection with only the hard total cap. (#2159) */
397
+ inlineCommentsPerCategory: number | null;
398
+ /** `review.min_finding_severity`: display-only floor for AI findings with a severity tier. Findings below the
399
+ * configured level are suppressed from inline comments — never from gate blockers. null (default, absent) ⇒ every
400
+ * finding shown = byte-identical behavior. (#2048) */
401
+ minFindingSeverity: ReviewFindingSeverity | null;
402
+ /** `review.max_findings`: optional caps on how many blocker/nit lines render in the unified review comment.
403
+ * Display-only — never removes a blocker from the gate decision. null sub-fields ⇒ no cap for that list.
404
+ * Default { blockers: null, nits: null } ⇒ byte-identical. (#2049) */
405
+ maxFindings: MaxFindingsConfig;
406
+ /** `review.comment_verbosity`: how much of the unified review comment's collapsible detail renders. `quiet`
407
+ * drops the Nits collapsible and every extra collapsible section (blockers/gate result/signals are never
408
+ * gated by this — only decorative detail is); `detailed` renders every collapsible pre-expanded. null/normal
409
+ * (default, absent) ⇒ byte-identical to today. Net-new vs the changed-files-summary (#1957) and effort-score
410
+ * (#1955) knobs. (#2047) */
411
+ commentVerbosity: CommentVerbosity | null;
412
+ /** `review.e2e_test_delivery` (#4197, part of the #4189 epic): how a `@gittensory generate-tests` result is
413
+ * delivered once `features.e2eTests` is on. `"comment"` (default, null/absent) posts the generated test as
414
+ * a reply comment only — no write access to the PR branch. `"commit"` pushes it as a real commit onto the
415
+ * PR's own head branch (git/trees -> git/commits -> a ref UPDATE, mirroring `repo-doc-pr.ts`'s write
416
+ * chokepoint) — a materially bigger blast radius, so it stays opt-in per repo even with e2eTests already
417
+ * on. `"commit"` mode is additionally blocked at runtime (regardless of this config) for a PR whose author
418
+ * is a confirmed Gittensor miner, to protect the external, upstream-computed score from ever including a
419
+ * maintainer-authored line the miner didn't write themselves — see `src/github/e2e-test-commit.ts`. */
420
+ e2eTestDelivery: E2eTestDeliveryMode | null;
421
+ /** `review.e2e_test_auto_trigger` (#4196, part of the #4189 epic): opts THIS repo into the `manifest_missing_tests`
422
+ * auto-trigger, which promotes that advisory finding into an actual unprompted generation run whenever a PR looks
423
+ * like it needs tests -- separate from `features.e2eTests`, which only unlocks the maintainer-initiated paths
424
+ * (the `@gittensory generate-tests` command and the PR-panel checkbox). Deliberately independent and OFF by
425
+ * default: enabling `e2eTests` for on-demand use must never, by itself, start firing generation unprompted on
426
+ * every under-tested PR (the exact loophole this field closes) -- a maintainer who *wants* the auto-trigger opts
427
+ * in explicitly per repo. null/false (default, absent) ⇒ the auto-trigger never fires, even with e2eTests on;
428
+ * true additionally requires e2eTests to already be enabled (this field alone does nothing). */
429
+ e2eTestAutoTrigger: boolean | null;
430
+ /** `review.path_instructions`: per-path natural-language guidance handed to the AI reviewer when the PR's
431
+ * changed files match the glob. Empty (default) ⇒ byte-identical reviewer prompt. Also consumed by
432
+ * AI-generated E2E test coverage (`resolveE2eTestGenInstructions` in `ai-e2e-test-gen.ts`, #4200) when
433
+ * that feature is enabled — the same maintainer-authored guidance steers both consumers, no separate
434
+ * test-generation-specific instructions schema. (#review-path-instructions) */
435
+ pathInstructions: ReviewPathInstruction[];
436
+ /** `review.instructions`: a repo-level natural-language brief handed to the AI reviewer on EVERY review (vs the
437
+ * per-path path_instructions) — the maintainer's conventions/voice for this repo. Bounded + public-safe at parse
438
+ * time (so it stays cost-cheap, unlike ingesting a whole CLAUDE.md). Also consumed by AI-generated E2E test
439
+ * coverage (#4200) for the same reason as pathInstructions above. null (default, absent) ⇒ byte-identical
440
+ * reviewer prompt. (#review-instructions) */
441
+ instructions: string | null;
442
+ /** `review.exclude_paths`: globs whose matching files are EXCLUDED from the AI review (diff + grounding + RAG)
443
+ * — generated/vendored/lockfiles the maintainer doesn't want reviewed. Empty (default) ⇒ every file is
444
+ * reviewed (byte-identical). Gate/slop/secret-scan are UNAFFECTED — this only narrows the AI review.
445
+ * (#review-exclude-paths) */
446
+ excludePaths: string[];
447
+ /** `review.path_filters`: include + `!`-negation globs that POSITIVELY scope the AI review AFTER
448
+ * `exclude_paths`. Include entries restrict to matching paths; leading `!` entries subtract matches.
449
+ * Both `*` and `**` cross slashes (see `compileManifestPathMatcher`). Empty (default) ⇒ every non-excluded
450
+ * file is reviewed (byte-identical). Gate/slop/secret-scan are UNAFFECTED. (#2043) */
451
+ pathFilters: string[];
452
+ /** `review.pre_merge_checks`: maintainer-declared DETERMINISTIC content assertions (title/description must
453
+ * contain a phrase, a label must be present), optionally gated to a path glob. Each FAILED check surfaces an
454
+ * advisory finding; a check with `enforce: true` becomes a hard gate blocker. Empty (default) ⇒ no finding
455
+ * (byte-identical). No AI judgment is involved. (#review-pre-merge-checks) */
456
+ preMergeChecks: PreMergeCheck[];
457
+ /** `review.auto_review`: deterministic eligibility filters that skip the AI review (never a gate failure).
458
+ * Empty/default ⇒ every PR is reviewed (byte-identical). (#1954 / #2038–#2041) */
459
+ autoReview: AutoReviewConfig;
460
+ /** `review.ai_model`: per-repo self-host reviewer model/effort overrides (claude-code / codex). Self-host only
461
+ * — a hosted (Workers-AI) repo ignores this entirely. All-null (default, absent) ⇒ the operator's global
462
+ * CLAUDE_AI_MODEL/CLAUDE_AI_EFFORT/CODEX_AI_MODEL/CODEX_AI_EFFORT env vars apply unchanged (byte-identical).
463
+ * (#selfhost-ai-model-override) */
464
+ aiModel: SelfHostAiModelConfig;
465
+ /** `review.visual`: per-repo before/after screenshot-capture config (#3609 preview / #3610 routes).
466
+ * All-empty (default, absent) ⇒ byte-identical to today (GitHub-native preview discovery, automatic
467
+ * file-to-route inference, built-in route cap). Only takes effect when the operator has also enabled
468
+ * GITTENSORY_REVIEW_SCREENSHOTS + the repo cutover allowlist — this config narrows/redirects that
469
+ * feature, it never turns it on by itself. */
470
+ visual: VisualConfig;
471
+ /** `review.linkedIssueSatisfaction`: how strictly a linked issue must actually be SATISFIED by the PR — `off`
472
+ * (default; not evaluated), `advisory` (surface a finding), or `block` (can become a hard blocker). CONFIG SLICE
473
+ * ONLY (#2173, for #1961): parsed + normalized here; the merge/close decision that reads this mode is a separate
474
+ * maintainer-only slice. null (default, absent) ⇒ byte-identical to today. */
475
+ linkedIssueSatisfaction: LinkedIssueSatisfactionMode | null;
476
+ /** Runtime provenance when the container-private shared base (`review.shared_config`, #2046) filled review
477
+ * fields from `GITTENSORY_REPO_CONFIG_DIR/_shared/.gittensory.yml`. Never parsed from maintainer YAML —
478
+ * set by the private-config loader only. null (default) ⇒ no shared overlay was applied. */
479
+ sharedConfigSource: string | null;
480
+ };
481
+ /** `review.linkedIssueSatisfaction` modes (#2173). `off` = not evaluated (same as unset). */
482
+ export declare const LINKED_ISSUE_SATISFACTION_MODES: readonly ["off", "advisory", "block"];
483
+ export type LinkedIssueSatisfactionMode = (typeof LINKED_ISSUE_SATISFACTION_MODES)[number];
484
+ /** `review.comment_verbosity` levels (#2047). `normal` = today's behavior (same as unset). */
485
+ export declare const COMMENT_VERBOSITY_LEVELS: readonly ["quiet", "normal", "detailed"];
486
+ export type CommentVerbosity = (typeof COMMENT_VERBOSITY_LEVELS)[number];
487
+ /** `review.e2e_test_delivery` modes (#4197). `comment` = today's behavior (same as unset). */
488
+ export declare const E2E_TEST_DELIVERY_MODES: readonly ["comment", "commit"];
489
+ export type E2eTestDeliveryMode = (typeof E2E_TEST_DELIVERY_MODES)[number];
490
+ /** `review.auto_review.cadence` (#one-shot-review-cadence). `one_shot` = the AI-generated content (main review,
491
+ * slop advisory, linked-issue satisfaction) is produced once per PR and never automatically regenerated
492
+ * afterward — not on a new push, not on CI-check completion, not on a scheduled sweep tick; only an explicit
493
+ * maintainer retrigger (the PR-panel checkbox or `@gittensory review` as a maintainer) spends a fresh call.
494
+ * `continuous` = the traditional behavior — every trigger re-runs AI content generation, subject to each
495
+ * feature's own head-SHA cache. Orthogonal to `aiReviewMode`'s enforcement-strictness axis (off/advisory/
496
+ * block) — the deterministic gate (CI status, mergeability, static-rule blockers) is NEVER affected by this
497
+ * and always re-evaluates on every pass regardless of cadence. */
498
+ export declare const AI_REVIEW_CADENCES: readonly ["one_shot", "continuous"];
499
+ export type AiReviewCadence = (typeof AI_REVIEW_CADENCES)[number];
500
+ /** Per-repo AI review eligibility knobs under `review.auto_review`. Unset fields are byte-identical defaults. */
501
+ export type AutoReviewConfig = {
502
+ /** `review.auto_review.skip_drafts`: when true, draft PRs skip AI review. null (default) ⇒ drafts reviewed as today. (#2038) */
503
+ skipDrafts: boolean | null;
504
+ /** `review.auto_review.cadence`: per-repo override of the AI review re-trigger cadence. null (default) ⇒
505
+ * inherit the operator's fleet-wide GITTENSORY_REVIEW_CONTINUOUS default (itself "one_shot" when unset).
506
+ * (#one-shot-review-cadence) */
507
+ cadence: AiReviewCadence | null;
508
+ /** `review.auto_review.ignore_authors`: author-login globs whose PRs skip AI review. Empty ⇒ every author. (#2039) */
509
+ ignoreAuthors: string[];
510
+ /** `review.auto_review.ignore_title_keywords`: case-insensitive title substrings that skip AI review. Empty ⇒ no skip. (#2040) */
511
+ ignoreTitleKeywords: string[];
512
+ /** `review.auto_review.skip_labels`: case-insensitive PR label names that skip AI review. Empty ⇒ no skip. (#2062) */
513
+ skipLabels: string[];
514
+ /** `review.auto_review.skip_docs_only`: when true, PRs whose every changed file classifies as docs skip AI review.
515
+ * null (default) ⇒ docs PRs reviewed as today. Empty changed-file list ⇒ NOT docs-only (fail-safe eligible). (#2063) */
516
+ skipDocsOnly: boolean | null;
517
+ /** `review.auto_review.max_added_lines`: skip AI review when total added lines exceed this cap. 0 (default) ⇒ no cap. (#2065) */
518
+ maxAddedLines: number;
519
+ /** `review.auto_review.max_files`: skip AI review when changed-file count exceeds this cap. 0 (default) ⇒ no cap. (#2065) */
520
+ maxFiles: number;
521
+ /** `review.auto_review.base_branches`: base-ref globs whose PRs ARE reviewed; empty/unset ⇒ every base. (#2041) */
522
+ baseBranches: string[];
523
+ /** `review.auto_review.auto_pause_after_reviewed_commits`: after N published AI reviews on this PR, pause further
524
+ * re-reviews. null/0 ⇒ byte-identical (re-review every sync). (#2042) */
525
+ autoPauseAfterReviewedCommits: number | null;
526
+ };
527
+ export type MaxFindingsConfig = {
528
+ blockers: number | null;
529
+ nits: number | null;
530
+ };
531
+ export declare const EMPTY_MAX_FINDINGS_CONFIG: MaxFindingsConfig;
532
+ export declare const EMPTY_AUTO_REVIEW_CONFIG: AutoReviewConfig;
533
+ /** Per-repo self-host reviewer model/effort overrides under `review.ai_model`. Each field independently overrides
534
+ * the matching global env var (CLAUDE_AI_MODEL / CLAUDE_AI_EFFORT / CODEX_AI_MODEL / CODEX_AI_EFFORT) for THIS
535
+ * repo only — it never widens what the operator's own env already permits, only narrows/redirects it, so a
536
+ * compromised repo config can change which model reviews it but not grant itself a new credential or provider.
537
+ * (#selfhost-ai-model-override) */
538
+ export type SelfHostAiModelConfig = {
539
+ /** `review.ai_model.claude_model`: overrides CLAUDE_AI_MODEL for this repo's claude-code reviewer. null (default) ⇒ the operator's global env var, then the provider's own default. */
540
+ claudeModel: string | null;
541
+ /** `review.ai_model.claude_effort`: overrides CLAUDE_AI_EFFORT for this repo's claude-code reviewer. null (default) ⇒ the operator's global env var, then "medium". */
542
+ claudeEffort: string | null;
543
+ /** `review.ai_model.codex_model`: overrides CODEX_AI_MODEL for this repo's codex reviewer. null (default) ⇒ the operator's global env var, then the account default. */
544
+ codexModel: string | null;
545
+ /** `review.ai_model.codex_effort`: overrides CODEX_AI_EFFORT for this repo's codex reviewer. null (default) ⇒ the operator's global env var, then "medium". */
546
+ codexEffort: string | null;
547
+ /** `review.ai_model.ollama_model` (#3902): overrides OLLAMA_AI_MODEL for this repo's ollama reviewer. null (default) ⇒ the operator's global env var, then the provider's own default. */
548
+ ollamaModel: string | null;
549
+ /** `review.ai_model.openai_model` (#3902): overrides OPENAI_AI_MODEL for this repo's openai reviewer. null (default) ⇒ the operator's global env var, then the provider's own default. */
550
+ openaiModel: string | null;
551
+ /** `review.ai_model.openai_compatible_model` (#3902): overrides OPENAI_COMPATIBLE_AI_MODEL for this repo's openai-compatible reviewer. null (default) ⇒ the operator's global env var, then the provider's own default. */
552
+ openaiCompatibleModel: string | null;
553
+ /** `review.ai_model.anthropic_model` (#3902): overrides ANTHROPIC_AI_MODEL for this repo's anthropic (BYOK Messages API) reviewer. null (default) ⇒ the operator's global env var, then the provider's own default. */
554
+ anthropicModel: string | null;
555
+ };
556
+ export declare const EMPTY_SELF_HOST_AI_MODEL_CONFIG: SelfHostAiModelConfig;
557
+ /** Per-repo before/after screenshot-capture config under `review.visual` (#3609 / #3610). Generic by design —
558
+ * every self-hoster wires their OWN repo's preview-deploy setup and route shape with config, not code. */
559
+ export type VisualConfig = {
560
+ /** `review.visual.production_url`: the repo's "before" production URL — e.g. `https://metagraph.sh` for a
561
+ * repo whose live site differs from the operator's own `PUBLIC_SITE_ORIGIN` env var (a single GLOBAL value
562
+ * with no per-repo awareness, correct for at most one repo on a multi-repo self-host instance). ALWAYS wins
563
+ * over `PUBLIC_SITE_ORIGIN` when set, mirroring `preview.url_template`'s precedence over GitHub-native
564
+ * discovery. null (default) ⇒ byte-identical to today (falls back to `PUBLIC_SITE_ORIGIN`). Validated at
565
+ * parse time against the same SSRF guard (`isSafeHttpUrl`) the renderer itself unconditionally applies. */
566
+ productionUrl: string | null;
567
+ preview: VisualPreviewConfig;
568
+ routes: VisualRoutesConfig;
569
+ themes: VisualTheme[];
570
+ /** `review.visual.gif`: capture a short scroll-through GIF (#3612) alongside the static before/after
571
+ * screenshots — evidence for scroll-linked behavior (parallax, reveal-on-scroll, a sticky header) that a
572
+ * single static shot can't show. Self-host only (see src/review/visual/scroll-gif.ts) and the heaviest
573
+ * capture mode this pipeline has (up to 6 extra renders per side) — false (default, every existing
574
+ * manifest) ⇒ byte-identical to today, no scroll frames captured at all. */
575
+ gif: boolean;
576
+ /** `review.visual.enabled` (#4083): a config-as-code override layered ON TOP OF the outer
577
+ * `GITTENSORY_REVIEW_SCREENSHOTS` / `GITTENSORY_REVIEW_REPOS` env-var gate, not a replacement for it. null
578
+ * (default, unset at every config layer) ⇒ defers entirely to that gate's own decision. `false` (settable at
579
+ * the global-default layer, or overridden per-repo) ⇒ forces capture off for this repo even when the env-var
580
+ * gate would otherwise allow it. `true` ⇒ no additional restriction — it does NOT bypass the env-var gate,
581
+ * it only opts back in at a layer where a global default of `false` disabled this repo. This is what lets an
582
+ * operator flip visual review on/off per-repo purely through the VPS config files, without a redeploy. */
583
+ enabled: boolean | null;
584
+ /** `review.visual.theme_storage_key` (#4109): the `localStorage` key the capture pipeline ALSO forces
585
+ * `theme` into (plus a reload) before rendering, for a target whose theming reads an explicit stored
586
+ * preference instead of consulting `prefers-color-scheme` — verified (against gittensory-ui's own
587
+ * dark-mode-only build) that `emulateMediaFeatures` alone has zero effect on that class of app, since it
588
+ * only changes what CSS media queries / `matchMedia` report. null (default) ⇒ no `localStorage` write, no
589
+ * reload — byte-identical to today. Only takes effect when `themes` is also configured; the key name is
590
+ * app-specific (there is no universal convention), so it is opaque, bounded, public-safe text, same shape
591
+ * as `review.ai_model`'s free-text fields. */
592
+ themeStorageKey: string | null;
593
+ /** `review.visual.actions_fallback` (#4112): when true, and ONLY when the existing GitHub-native discovery
594
+ * chain (Deployments API / commit checks / cloudflare-bot PR comment / an explicit `preview.url_template`)
595
+ * finds no preview at all for this PR, dispatch `.github/workflows/visual-capture-fallback.yml` -- a
596
+ * fork-safe GitHub Actions job that builds, serves, and screenshots the PR's own code with zero secrets --
597
+ * and use its captured PNGs as the "after" shot instead. false (default) ⇒ byte-identical to today (no
598
+ * dispatch, no change to the discovery order). Requires the target repo to have that workflow file present
599
+ * (see the workflow's own header comment for setup); a repo without it just never gets a fallback run, same
600
+ * as leaving this unset. (#3607 visual-capture convergence epic) */
601
+ actionsFallback: boolean;
602
+ };
603
+ /** A `prefers-color-scheme` value the capture pipeline can emulate before rendering (#3678). */
604
+ export type VisualTheme = "light" | "dark";
605
+ export type VisualPreviewConfig = {
606
+ /** `review.visual.preview.url_template`: the repo's "after" preview URL, with `{number}` (PR number),
607
+ * `{head_sha}` (full commit SHA), and `{head_sha_short}` (first 7 chars) placeholders substituted at
608
+ * capture time — e.g. `https://pr-{number}.myapp.workers.dev`. ALWAYS wins over GitHub-native preview
609
+ * discovery (the Deployments API / commit checks / cloudflare-bot PR comment) when set — an explicit,
610
+ * maintainer-configured template is a stronger signal than inference, and is the only option for a
611
+ * provider (e.g. Cloudflare Workers Builds' non-production branch builds) that doesn't surface a
612
+ * GitHub-visible deployment at all. null (default) ⇒ byte-identical to today (discovery unchanged).
613
+ * Validated at parse time against the same SSRF guard the renderer itself applies (isSafeHttpUrl) with
614
+ * placeholders substituted for a dummy value, so a malformed template warns at config-read time instead
615
+ * of only failing silently at render time — this is redundant with (not a replacement for) the
616
+ * renderer's own unconditional isSafeHttpUrl check on every resolved URL, regardless of source. */
617
+ urlTemplate: string | null;
618
+ };
619
+ export type VisualRoutesConfig = {
620
+ /** `review.visual.routes.paths`: an explicit, always-screenshotted route list. When non-empty, this
621
+ * REPLACES automatic file-to-route inference entirely — for repos whose routing convention isn't
622
+ * gittensory-ui's TanStack file-based one, an explicit list is simpler and more robust than trying to
623
+ * infer one. Empty (default) ⇒ automatic inference (falling back to "/" when nothing matches). */
624
+ paths: string[];
625
+ /** `review.visual.routes.max_routes`: overrides the built-in cap (2) on how many routes get screenshotted
626
+ * per PR. null (default) ⇒ built-in default. Applies whether routes come from `paths` above or from
627
+ * automatic inference. */
628
+ maxRoutes: number | null;
629
+ };
630
+ export declare const EMPTY_VISUAL_CONFIG: VisualConfig;
631
+ /** One `review.path_instructions[]` entry: a manifest path glob + the public-safe instructions to apply when a
632
+ * changed file matches it. */
633
+ export type ReviewPathInstruction = {
634
+ path: string;
635
+ instructions: string;
636
+ };
637
+ /** One `review.pre_merge_checks[]` entry — a DETERMINISTIC pre-merge assertion. `whenPaths` (empty ⇒ always
638
+ * applies) gates the check to PRs that touch a matching path. The check PASSES only when EVERY configured
639
+ * assertion holds: the PR title contains `titleContains`, the body contains `descriptionContains`, and the
640
+ * `requireLabel` label is present (case-insensitive substring / label match). `enforce` ⇒ a failure is a hard
641
+ * gate blocker; default (false) ⇒ advisory only. All strings are public-safe-filtered at parse time. */
642
+ export type PreMergeCheck = {
643
+ name: string;
644
+ whenPaths: string[];
645
+ titleContains: string | null;
646
+ descriptionContains: string | null;
647
+ requireLabel: string | null;
648
+ enforce: boolean;
649
+ };
650
+ /**
651
+ * Normalized maintainer focus manifest. Repo owners declare which work areas are wanted,
652
+ * preferred, and how PRs should present validation. Path-based manual review is intentionally
653
+ * not part of this manifest anymore; use `settings.hardGuardrailGlobs` for that single
654
+ * authoritative control. `maintainerNotes` are private review context and must never reach a public
655
+ * GitHub surface; `publicNotes` are explicitly opted into public output by the maintainer.
656
+ */
657
+ export type FocusManifest = {
658
+ present: boolean;
659
+ source: FocusManifestSource;
660
+ wantedPaths: string[];
661
+ preferredLabels: string[];
662
+ linkedIssuePolicy: FocusManifestLinkedIssuePolicy;
663
+ testExpectations: string[];
664
+ issueDiscoveryPolicy: FocusManifestIssueDiscoveryPolicy;
665
+ maintainerNotes: string[];
666
+ publicNotes: string[];
667
+ gate: FocusManifestGateConfig;
668
+ settings: FocusManifestSettings;
669
+ review: FocusManifestReviewConfig;
670
+ features: FocusManifestFeaturesConfig;
671
+ experimental: FocusManifestExperimentalConfig;
672
+ contentLane: FocusManifestContentLaneConfig;
673
+ repoDocGeneration: FocusManifestRepoDocGenerationConfig;
674
+ reviewRecap: FocusManifestReviewRecapConfig;
675
+ maintainerRecap: FocusManifestMaintainerRecapConfig;
676
+ warnings: string[];
677
+ };
678
+ export type FocusManifestFinding = {
679
+ code: "manifest_off_focus" | "manifest_preferred_path" | "manifest_missing_preferred_label" | "manifest_linked_issue_required" | "manifest_linked_issue_preferred" | "manifest_missing_tests" | "manifest_issue_discovery_discouraged" | "manifest_malformed";
680
+ severity: "info" | "warning" | "critical";
681
+ title: string;
682
+ detail: string;
683
+ action?: string | undefined;
684
+ };
685
+ export type FocusManifestGuidance = {
686
+ present: boolean;
687
+ source: FocusManifestSource;
688
+ linkedIssuePolicy: FocusManifestLinkedIssuePolicy;
689
+ issueDiscoveryPolicy: FocusManifestIssueDiscoveryPolicy;
690
+ matchedWantedPaths: string[];
691
+ preferredLabelHits: string[];
692
+ findings: FocusManifestFinding[];
693
+ publicNextSteps: string[];
694
+ warnings: string[];
695
+ summary: string;
696
+ };
697
+ export declare const MAX_FOCUS_MANIFEST_BYTES: number;
698
+ /**
699
+ * Public-safe redaction guard shared with the local-branch packet renderer. Public manifest
700
+ * text must not leak reward, wallet/key, ranking, or local filesystem path material.
701
+ */
702
+ export declare function isFocusManifestPublicSafe(text: string): boolean;
703
+ /** `gate.readiness.mode` (and its `settings.qualityGateMode` alias below) is documented and parsed as the shared
704
+ * off/advisory/block tri-state, but buildQualityGateWarning (src/rules/advisory.ts) always produces a
705
+ * warning-severity finding — never a blocker — and isConfiguredGateBlocker has no branch for it: readiness/
706
+ * quality is intentionally informational-only and can never hard-block a PR. Without this, a maintainer who
707
+ * sets `mode: block` believes a real quality floor is enforced when the effective behavior is silently
708
+ * advisory-only (#2267). Downgrade "block" to "advisory" here, with a clear deprecation warning, so the parsed
709
+ * config always matches what the gate actually does. Exported so the settings-write API routes (the
710
+ * dashboard/API path for the SAME `qualityGateMode` field) can apply the identical downgrade before persisting. */
711
+ export declare function normalizeReadinessGateMode(value: JsonValue | undefined, field: string, warnings: string[]): GateRuleMode | null;
712
+ /**
713
+ * Serialize a gate config back into the parse-compatible `gate:` shape so a cached manifest snapshot
714
+ * round-trips through {@link parseGateConfig} unchanged. Returns null when nothing is configured.
715
+ */
716
+ export declare function gateConfigToJson(gate: FocusManifestGateConfig): JsonValue;
717
+ /** Serialize a features config back into the parse-compatible `features:` shape so a cached snapshot round-trips
718
+ * through {@link parseFeaturesConfig} unchanged. Returns null when nothing is configured. */
719
+ export declare function featuresConfigToJson(features: FocusManifestFeaturesConfig): JsonValue;
720
+ /** Serialize an experimental config back into the parse-compatible `experimental:` shape so a cached snapshot
721
+ * round-trips through {@link parseExperimentalConfig} unchanged. Returns null when nothing is configured. */
722
+ export declare function experimentalConfigToJson(experimental: FocusManifestExperimentalConfig): JsonValue;
723
+ /** Serialize a contentLane config back into the parse-compatible `contentLane:` shape so a cached snapshot
724
+ * round-trips through {@link parseContentLaneConfig} unchanged. Returns null when nothing is configured. */
725
+ export declare function contentLaneConfigToJson(contentLane: FocusManifestContentLaneConfig): JsonValue;
726
+ /** Serialize a repoDocGeneration config back into the parse-compatible shape so a cached snapshot round-trips
727
+ * through {@link parseRepoDocGenerationConfig} unchanged. Returns null when nothing is configured. */
728
+ export declare function repoDocGenerationConfigToJson(config: FocusManifestRepoDocGenerationConfig): JsonValue;
729
+ /** Serialize a reviewRecap config back into the parse-compatible shape so a cached snapshot round-trips
730
+ * through {@link parseReviewRecapConfig} unchanged. Returns null when nothing is configured. */
731
+ export declare function reviewRecapConfigToJson(config: FocusManifestReviewRecapConfig): JsonValue;
732
+ /** Serialize a maintainerRecap config back into the parse-compatible shape so a cached snapshot round-trips
733
+ * through {@link parseMaintainerRecapConfig} unchanged. Returns null when nothing is configured. */
734
+ export declare function maintainerRecapConfigToJson(config: FocusManifestMaintainerRecapConfig): JsonValue;
735
+ /** Serialize the settings override for the cache round-trip; returns null when nothing is set. */
736
+ export declare function settingsOverrideToJson(settings: FocusManifestSettings): JsonValue;
737
+ /** Overlay a higher-priority `review:` config onto a shared/base layer (#2046). Per-field: override wins when set;
738
+ * base fills gaps; defaults stay byte-identical. `sharedConfigSource` on the override is preserved when present. */
739
+ export declare function overlayReviewConfig(base: FocusManifestReviewConfig, override: FocusManifestReviewConfig): FocusManifestReviewConfig;
740
+ /** Parse a raw `review:` mapping value. Exported for the private-config shared overlay (#2046). */
741
+ export declare function parseReviewConfigMapping(value: JsonValue | undefined, warnings: string[]): FocusManifestReviewConfig;
742
+ /** Serialize the review config for the cache round-trip; returns null when nothing is set. */
743
+ export declare function reviewConfigToJson(review: FocusManifestReviewConfig): JsonValue;
744
+ /**
745
+ * Resolve the `review.path_instructions` that APPLY to a PR — those whose glob matches at least one changed path
746
+ * — into a single prompt section for the AI reviewer, or "" when none match (so the prompt stays byte-identical).
747
+ * Pure; uses the same manifest path-glob semantics (`matchesManifestPath`) as the rest of the manifest. Capped to
748
+ * keep the prompt bounded. (#review-path-instructions)
749
+ */
750
+ export declare function parseFocusManifest(raw: unknown, source?: FocusManifestSource): FocusManifest;
751
+ /**
752
+ * Parse raw manifest file/record content (JSON or YAML). Malformed content degrades to an empty
753
+ * manifest with a warning rather than throwing, so a broken `.gittensory` config never breaks analysis.
754
+ */
755
+ export declare function parseFocusManifestContent(content: string | null | undefined, source?: FocusManifestSource): FocusManifest;
756
+ /**
757
+ * Format a manifest's parse `warnings[]` into one grouped, deduped, order-preserving notice for the review
758
+ * surface — an acceptance criterion of #1670: an invalid/malformed `.gittensory.yml` value should fail
759
+ * clearly instead of silently falling back to a default. Empty/no warnings ⇒ `null` (byte-identical, no
760
+ * notice). Pure; reuses the warnings every parser already accumulates rather than a parallel schema. (#2056)
761
+ */
762
+ export declare function formatManifestValidationNotice(warnings: string[]): string | null;
763
+ /**
764
+ * Match a changed path against a manifest path pattern. Supports exact paths, directory
765
+ * prefixes (`src/` or `src`), and `*` wildcards (`**` collapses to `*`).
766
+ */
767
+ export declare function matchesManifestPath(path: string, pattern: string): boolean;
768
+ export type FocusManifestLanePreference = "preferred" | "neutral" | "discouraged";
769
+ export type FocusManifestPolicyContributionLane = {
770
+ id: string;
771
+ preference: "preferred" | "neutral" | "discouraged";
772
+ title: string;
773
+ summary: string;
774
+ preferredPaths: string[];
775
+ discouragedPaths: string[];
776
+ validationExpectations: string[];
777
+ publicNotes: string[];
778
+ };
779
+ export type FocusManifestPolicyLabelPolicy = {
780
+ preferredLabels: string[];
781
+ required: boolean;
782
+ };
783
+ export type FocusManifestPolicyValidation = {
784
+ expectations: string[];
785
+ linkedIssuePolicy: FocusManifestLinkedIssuePolicy;
786
+ };
787
+ export type FocusManifestPolicy = {
788
+ repoFullName: string;
789
+ generatedAt: string;
790
+ source: FocusManifestSource;
791
+ present: boolean;
792
+ publicSafe: {
793
+ contributionLanes: FocusManifestPolicyContributionLane[];
794
+ labelPolicy: FocusManifestPolicyLabelPolicy;
795
+ validation: FocusManifestPolicyValidation;
796
+ issueDiscoveryPolicy: FocusManifestIssueDiscoveryPolicy;
797
+ publicNotes: string[];
798
+ readinessWarnings: string[];
799
+ entryGuidance: string[];
800
+ summary: string;
801
+ };
802
+ authenticated: {
803
+ manifestSource: FocusManifestSource;
804
+ privateNoteCount: number;
805
+ manifestWarningCount: number;
806
+ parseWarnings: string[];
807
+ readinessWarnings: string[];
808
+ maintainerContext: string[];
809
+ };
810
+ };
811
+ /**
812
+ * Compile a normalized {@link FocusManifest} into a deterministic, machine-readable
813
+ * {@link FocusManifestPolicy}. Public-safe fields are segregated from authenticated
814
+ * (owner-only) fields. No reward, wallet, hotkey, raw trust, or private scoring
815
+ * language is allowed in public-safe output — unsafe strings are silently dropped.
816
+ *
817
+ * `repoFullName` is optional — when omitted it defaults to an empty string. Callers
818
+ * that persist the policy should supply the full name; single-manifest analysis
819
+ * callers may omit it.
820
+ */
821
+ export declare function compileFocusManifestPolicy(manifest: FocusManifest, options?: {
822
+ generatedAt?: string;
823
+ }): FocusManifestPolicy;
824
+ export declare function compileFocusManifestPolicy(repoFullName: string, manifest: FocusManifest, options?: {
825
+ generatedAt?: string;
826
+ }): FocusManifestPolicy;