@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,98 @@
1
+ /**
2
+ * Duplicate-winner adjudication (#dup-winner). Flag-gated by GITTENSORY_DUPLICATE_WINNER.
3
+ *
4
+ * When several OPEN PRs link the same issue (a duplicate cluster), the legacy behavior gate-blocks +
5
+ * auto-closes EVERY sibling as a duplicate — no winner survives. With the flag ON, exactly ONE winner is
6
+ * spared: the earliest claimant. Sparse legacy rows that do not yet have claim timing fail closed so unknown
7
+ * ordering cannot arbitrarily suppress duplicate evidence. Only the LOSERS are blocked/closed; the winner
8
+ * still must pass CI / conflict / gate / linked-issue / slop on its OWN merits.
9
+ *
10
+ * This module is PURE — no IO, no Date, no random — so the same inputs always yield the same verdict and the
11
+ * caller can compute the winner ONCE per review run and thread the result boolean consistently into every
12
+ * surface (advisory finding, close reason, slop, panels), so they agree by construction.
13
+ *
14
+ * ELECTION ORDER: compare `linkedIssueClaimedAt`, the time gittensory first observed the PR claiming
15
+ * the issue. GitHub `pull_request.created_at` is intentionally not an ordering signal here: contributors can
16
+ * edit an old placeholder PR to add a linked issue later, so creation time would let backdated claims steal
17
+ * duplicate-winner credit from the PR that actually claimed the issue first. Sparse legacy rows that lack
18
+ * claim timing keep failing closed so unknown ordering cannot suppress duplicate evidence.
19
+ *
20
+ * INVARIANT (the caller MUST honor it): {@link openSiblingNumbers} carries OPEN-only sibling PR numbers. The
21
+ * existing sources already exclude closed/merged PRs. Once the winner closes (e.g. red CI), it leaves the open
22
+ * set and the next-earliest OPEN claimant becomes the winner on re-eval — no permanently-orphaned cluster.
23
+ *
24
+ * SECOND CONSUMER (#2278): this module is intentionally engine-hosted (not `src/`-only) because its election
25
+ * logic is reusable for the miner's own soft-claim adjudication — deciding which of several miners claiming
26
+ * the same issue proceeds. A future contributor wiring the miner's local claim ledger should import this
27
+ * module rather than reimplementing the election rule, so both the maintainer gate and the miner agree on
28
+ * exactly one winner by construction.
29
+ */
30
+ /**
31
+ * True iff `prNumber` is the cluster winner: the minimum of `{prNumber} ∪ openSiblingNumbers`. An empty
32
+ * sibling list ⇒ the PR is alone in (or out of) the cluster ⇒ winner. A sibling list that happens to contain
33
+ * `prNumber` itself is harmless — the comparison is still min-based.
34
+ *
35
+ * @deprecated Use {@link isDuplicateClusterWinnerByClaim}. PR-number election is retained only for legacy
36
+ * compatibility callers that do not have claim timestamps.
37
+ */
38
+ export function isDuplicateClusterWinner(prNumber, openSiblingNumbers) {
39
+ for (const sibling of openSiblingNumbers) {
40
+ if (sibling < prNumber)
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ /**
46
+ * True iff `pr` is the earliest-elected claimant in the open duplicate cluster (see the module doc's
47
+ * "ELECTION ORDER" note). Sparse legacy rows fail closed; ties between equally-ordered members use PR number.
48
+ */
49
+ export function isDuplicateClusterWinnerByClaim(pr, openSiblings) {
50
+ if (openSiblings.length === 0)
51
+ return true;
52
+ for (const sibling of openSiblings) {
53
+ if (!prPrecedesSibling(pr, sibling))
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ /**
59
+ * True iff `pr` is ordered at or ahead of `sibling` for cluster-winner purposes. Only the observed linked-issue
60
+ * claim time participates in the election; `createdAt` is deliberately ignored because an older PR can claim a
61
+ * linked issue later by editing its body.
62
+ */
63
+ function prPrecedesSibling(pr, sibling) {
64
+ const prClaim = claimTimeMs(pr.linkedIssueClaimedAt);
65
+ if (prClaim === null)
66
+ return false;
67
+ const siblingClaim = claimTimeMs(sibling.linkedIssueClaimedAt);
68
+ if (siblingClaim === null)
69
+ return false;
70
+ if (siblingClaim < prClaim)
71
+ return false;
72
+ if (siblingClaim === prClaim && sibling.number < pr.number)
73
+ return false;
74
+ return true;
75
+ }
76
+ /**
77
+ * The winning PR number among `pr` and its open duplicate siblings, or `null` when the election is not
78
+ * determinable (mirrors {@link isDuplicateClusterWinnerByClaim}'s fail-closed semantics — this never guesses a
79
+ * specific winner when the ordering data is too sparse/ambiguous to be sure). Used only for DISPLAY (naming the
80
+ * winner in a loser's close comment, #dup-winner-credit) — the close/hold decision for any given PR is still
81
+ * driven directly by {@link isDuplicateClusterWinnerByClaim}, not by this function's return value.
82
+ */
83
+ export function resolveDuplicateClusterWinnerNumber(pr, openSiblings) {
84
+ if (isDuplicateClusterWinnerByClaim(pr, openSiblings))
85
+ return pr.number;
86
+ for (const sibling of openSiblings) {
87
+ const rest = openSiblings.filter((other) => other.number !== sibling.number);
88
+ if (isDuplicateClusterWinnerByClaim(sibling, [pr, ...rest]))
89
+ return sibling.number;
90
+ }
91
+ return null;
92
+ }
93
+ function claimTimeMs(value) {
94
+ if (!value)
95
+ return null;
96
+ const parsed = Date.parse(value);
97
+ return Number.isFinite(parsed) ? parsed : null;
98
+ }
@@ -0,0 +1,27 @@
1
+ export type FeasibilityClaimStatus = "unclaimed" | "claimed" | "solved" | "unknown";
2
+ export type FeasibilityDuplicateClusterRisk = "none" | "low" | "medium" | "high";
3
+ export type FeasibilityIssueStatus = "ready" | "needs_proof" | "hold" | "do_not_use" | "duplicate" | "invalid" | "missing";
4
+ export type FeasibilityGateInput = {
5
+ /** Whether cached metadata resolved a target issue. Defaults to true when omitted. */
6
+ found?: boolean | undefined;
7
+ claimStatus: FeasibilityClaimStatus;
8
+ duplicateClusterRisk: FeasibilityDuplicateClusterRisk;
9
+ issueStatus: FeasibilityIssueStatus;
10
+ };
11
+ export type FeasibilityVerdict = "go" | "raise" | "avoid";
12
+ export type FeasibilityGateResult = {
13
+ verdict: FeasibilityVerdict;
14
+ avoidReasons: readonly string[];
15
+ raiseReasons: readonly string[];
16
+ summary: string;
17
+ };
18
+ /** Pure feasibility verdict from claim, duplicate-cluster, and issue-status signals. */
19
+ export declare function buildFeasibilityVerdict(input: FeasibilityGateInput): FeasibilityGateResult;
20
+ /** Map a pre-start check report into the feasibility composer's input shape (parity helper for tests/callers). */
21
+ export declare function feasibilityInputFromPreStartCheck(report: {
22
+ found: boolean;
23
+ claimStatus: FeasibilityClaimStatus;
24
+ duplicateClusterRisk: FeasibilityDuplicateClusterRisk;
25
+ issueQualityStatus?: "ready" | "needs_proof" | "hold" | "do_not_use" | undefined;
26
+ lifecycle?: string | undefined;
27
+ }): FeasibilityGateInput;
@@ -0,0 +1,86 @@
1
+ // Feasibility-gate composer (pure).
2
+ //
3
+ // Metadata-only verdict over the three discriminants the analyze-phase feasibility gate actually branches on:
4
+ // claim status, duplicate-cluster risk, and issue quality/lifecycle status. Composes the same go/raise/avoid
5
+ // decision as the pre-start check's core recommendation logic without pulling in repo records or GitHub caches.
6
+ function collectAvoidReasons(input) {
7
+ // Collect every avoid trigger independently — never fold with ??/|| so two simultaneous avoid signals both surface.
8
+ const reasons = [];
9
+ if (input.claimStatus === "solved")
10
+ reasons.push("claim_status_solved");
11
+ if (input.issueStatus === "do_not_use")
12
+ reasons.push("issue_quality_do_not_use");
13
+ if (input.issueStatus === "duplicate")
14
+ reasons.push("issue_lifecycle_duplicate");
15
+ if (input.issueStatus === "invalid")
16
+ reasons.push("issue_lifecycle_invalid");
17
+ if (input.duplicateClusterRisk === "high")
18
+ reasons.push("duplicate_cluster_high");
19
+ return reasons;
20
+ }
21
+ function collectRaiseReasons(input, found) {
22
+ const reasons = [];
23
+ if (!found)
24
+ reasons.push("target_not_found");
25
+ if (input.duplicateClusterRisk === "medium")
26
+ reasons.push("duplicate_cluster_medium");
27
+ if (input.claimStatus === "claimed")
28
+ reasons.push("claim_status_claimed");
29
+ if (input.issueStatus === "needs_proof" || input.issueStatus === "hold")
30
+ reasons.push("issue_quality_uncertain");
31
+ if (input.issueStatus === "missing")
32
+ reasons.push("issue_missing");
33
+ return reasons;
34
+ }
35
+ /** Pure feasibility verdict from claim, duplicate-cluster, and issue-status signals. */
36
+ export function buildFeasibilityVerdict(input) {
37
+ const found = input.found ?? true;
38
+ const avoidReasons = collectAvoidReasons(input);
39
+ if (avoidReasons.length > 0) {
40
+ return {
41
+ verdict: "avoid",
42
+ avoidReasons,
43
+ raiseReasons: [],
44
+ summary: `Avoid: ${avoidReasons.join(", ")}.`,
45
+ };
46
+ }
47
+ const raiseReasons = collectRaiseReasons(input, found);
48
+ if (raiseReasons.length > 0) {
49
+ return {
50
+ verdict: "raise",
51
+ avoidReasons: [],
52
+ raiseReasons,
53
+ summary: `Raise: ${raiseReasons.join(", ")}.`,
54
+ };
55
+ }
56
+ return {
57
+ verdict: "go",
58
+ avoidReasons: [],
59
+ raiseReasons: [],
60
+ summary: "Go: no blocking feasibility signal detected.",
61
+ };
62
+ }
63
+ /** Map a pre-start check report into the feasibility composer's input shape (parity helper for tests/callers). */
64
+ export function feasibilityInputFromPreStartCheck(report) {
65
+ let issueStatus;
66
+ if (!report.found)
67
+ issueStatus = "missing";
68
+ else if (report.lifecycle === "duplicate")
69
+ issueStatus = "duplicate";
70
+ else if (report.lifecycle === "invalid")
71
+ issueStatus = "invalid";
72
+ else if (report.issueQualityStatus === "do_not_use")
73
+ issueStatus = "do_not_use";
74
+ else if (report.issueQualityStatus === "needs_proof")
75
+ issueStatus = "needs_proof";
76
+ else if (report.issueQualityStatus === "hold")
77
+ issueStatus = "hold";
78
+ else
79
+ issueStatus = "ready";
80
+ return {
81
+ found: report.found,
82
+ claimStatus: report.claimStatus,
83
+ duplicateClusterRisk: report.duplicateClusterRisk,
84
+ issueStatus,
85
+ };
86
+ }
@@ -0,0 +1,116 @@
1
+ import type { ObjectiveAnchorScore } from "./objective-anchor.js";
2
+ import type { PairwiseCalibrationScore } from "./pairwise-calibration.js";
3
+ export type FindingSeverityTier = "blocker" | "warning" | "advisory" | "nit";
4
+ export type FindingSeverityCalibrationManifest = {
5
+ miner?: {
6
+ calibration?: {
7
+ /** Explicit maintainer opt-in. Default false. */
8
+ shareStructuredFindingSeverity?: unknown;
9
+ /** Optional weight for the structured finding-severity signal when composed into a replay score. */
10
+ structuredFindingSeverityWeight?: unknown;
11
+ } | null;
12
+ } | null;
13
+ calibration?: {
14
+ /** Back-compat/future-friendly alias, still explicit and default-off. */
15
+ shareStructuredFindingSeverity?: unknown;
16
+ structuredFindingSeverityWeight?: unknown;
17
+ } | null;
18
+ };
19
+ export type FindingSeverityCalibrationConfig = {
20
+ shareStructuredFindingSeverity: boolean;
21
+ structuredFindingSeverityWeight: number;
22
+ warnings: string[];
23
+ };
24
+ export type FindingSeverityTierInput = {
25
+ tier: FindingSeverityTier | string;
26
+ /** Total findings the review raised at this tier. */
27
+ total: number;
28
+ /** How many of those were subsequently confirmed (true positives). Clamped to `[0, total]`. */
29
+ confirmed?: number | undefined;
30
+ /** Optional 0..1 confidence in the confirmation labelling for this tier. */
31
+ confidence?: number | undefined;
32
+ };
33
+ export type FindingSeverityCalibrationSignalInput = {
34
+ repoFullName: string;
35
+ replayRunId: string;
36
+ reviewRunId: string;
37
+ optedIn: boolean;
38
+ observedAt?: string | undefined;
39
+ tiers: readonly FindingSeverityTierInput[];
40
+ };
41
+ export type FindingSeverityTierSignal = {
42
+ tier: FindingSeverityTier;
43
+ total: number;
44
+ confirmed: number;
45
+ confirmationRate: number;
46
+ weight: number;
47
+ score: number;
48
+ };
49
+ export type FindingSeverityCalibrationSignal = {
50
+ repoFullName: string;
51
+ replayRunId: string;
52
+ reviewRunId: string;
53
+ observedAt: string | null;
54
+ tiers: FindingSeverityTierSignal[];
55
+ score: number;
56
+ };
57
+ export type FindingSeverityCalibrationIngestion = {
58
+ accepted: FindingSeverityCalibrationSignal[];
59
+ rejected: Array<{
60
+ repoFullName: string;
61
+ replayRunId: string;
62
+ reviewRunId: string;
63
+ reason: "not_opted_in" | "empty_tiers" | "invalid_repo" | "invalid_run_id";
64
+ }>;
65
+ };
66
+ export type FindingSeverityCalibrationWeights = {
67
+ objectiveAnchor?: number | undefined;
68
+ pairwiseJudge?: number | undefined;
69
+ structuredFindingSeverity?: number | undefined;
70
+ };
71
+ export type FindingSeverityCompositeCalibrationScore = {
72
+ compositeScore: number;
73
+ objectiveAnchorScore: number;
74
+ pairwiseJudgeScore: number | null;
75
+ structuredFindingSeverityScore: number | null;
76
+ weights: {
77
+ objectiveAnchor: number;
78
+ pairwiseJudge: number;
79
+ structuredFindingSeverity: number;
80
+ };
81
+ audit: {
82
+ contributingRepos: Array<{
83
+ repoFullName: string;
84
+ replayRunId: string;
85
+ reviewRunId: string;
86
+ observedAt: string | null;
87
+ score: number;
88
+ tiers: FindingSeverityTierSignal[];
89
+ }>;
90
+ rejected: FindingSeverityCalibrationIngestion["rejected"];
91
+ };
92
+ };
93
+ /**
94
+ * Resolve the explicit per-repo opt-in from a parsed `.gittensory.yml`-style object. Default is opted out. The
95
+ * preferred path is `miner.calibration.shareStructuredFindingSeverity`; `calibration.shareStructuredFindingSeverity`
96
+ * is accepted as a narrow alias so private-config surfaces can place the field at top level if needed.
97
+ */
98
+ export declare function resolveFindingSeverityCalibrationConfig(manifest: FindingSeverityCalibrationManifest | Record<string, unknown> | null | undefined): FindingSeverityCalibrationConfig;
99
+ /**
100
+ * Ingest only currently opted-in structured finding-severity signals. The opt-in check happens at ingestion time, so
101
+ * a maintainer opt-out immediately prevents additional calibration rows from contributing even if older collected
102
+ * data exists elsewhere.
103
+ */
104
+ export declare function ingestFindingSeverityCalibrationSignals(signals: readonly FindingSeverityCalibrationSignalInput[]): FindingSeverityCalibrationIngestion;
105
+ export declare function computeFindingSeverityCompositeCalibrationScore(input: {
106
+ objectiveAnchor: number | ObjectiveAnchorScore;
107
+ pairwise: number | PairwiseCalibrationScore | null;
108
+ findingSeverity: FindingSeverityCalibrationIngestion | readonly FindingSeverityCalibrationSignalInput[];
109
+ weights?: FindingSeverityCalibrationWeights | undefined;
110
+ }): FindingSeverityCompositeCalibrationScore;
111
+ /**
112
+ * Render a deterministic, public-safe Markdown report for a structured finding-severity calibration result. The
113
+ * report is local-run evidence: it includes aggregate scores, normalized weights, opted-in contributors, and rejected
114
+ * rows, but never accepts or emits raw review text or private scoring fields.
115
+ */
116
+ export declare function renderFindingSeverityCalibrationAuditMarkdown(result: FindingSeverityCompositeCalibrationScore): string;