@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.
- package/CHANGELOG.md +94 -0
- package/LICENSE +661 -0
- package/README.md +676 -0
- package/dist/advisory/gate-advisory.d.ts +151 -0
- package/dist/advisory/gate-advisory.js +512 -0
- package/dist/ai-policy-map.d.ts +97 -0
- package/dist/ai-policy-map.js +377 -0
- package/dist/ams-policy-spec.d.ts +56 -0
- package/dist/ams-policy-spec.js +165 -0
- package/dist/calibration-dashboard.d.ts +23 -0
- package/dist/calibration-dashboard.js +65 -0
- package/dist/calibration-trend.d.ts +40 -0
- package/dist/calibration-trend.js +60 -0
- package/dist/contributor-fit.d.ts +24 -0
- package/dist/contributor-fit.js +50 -0
- package/dist/discovery-index-contract.d.ts +74 -0
- package/dist/discovery-index-contract.js +184 -0
- package/dist/discovery-soft-claim.d.ts +35 -0
- package/dist/discovery-soft-claim.js +50 -0
- package/dist/duplicate-winner.d.ts +57 -0
- package/dist/duplicate-winner.js +98 -0
- package/dist/feasibility.d.ts +27 -0
- package/dist/feasibility.js +86 -0
- package/dist/finding-severity-calibration.d.ts +116 -0
- package/dist/finding-severity-calibration.js +457 -0
- package/dist/fleet-run-manifest.d.ts +41 -0
- package/dist/fleet-run-manifest.js +151 -0
- package/dist/focus-manifest/guidance.d.ts +21 -0
- package/dist/focus-manifest/guidance.js +222 -0
- package/dist/focus-manifest.d.ts +826 -0
- package/dist/focus-manifest.js +2730 -0
- package/dist/gate-verdict-calibration.d.ts +111 -0
- package/dist/gate-verdict-calibration.js +411 -0
- package/dist/github/constants.d.ts +1 -0
- package/dist/github/constants.js +1 -0
- package/dist/github/sanitize-public-comment.d.ts +1 -0
- package/dist/github/sanitize-public-comment.js +26 -0
- package/dist/goal-model.d.ts +7 -0
- package/dist/goal-model.js +87 -0
- package/dist/governor/action-mode.d.ts +55 -0
- package/dist/governor/action-mode.js +74 -0
- package/dist/governor/budget-cap.d.ts +49 -0
- package/dist/governor/budget-cap.js +34 -0
- package/dist/governor/chokepoint.d.ts +58 -0
- package/dist/governor/chokepoint.js +269 -0
- package/dist/governor/kill-switch.d.ts +38 -0
- package/dist/governor/kill-switch.js +62 -0
- package/dist/governor/rate-limit.d.ts +40 -0
- package/dist/governor/rate-limit.js +63 -0
- package/dist/governor/reputation-throttle.d.ts +50 -0
- package/dist/governor/reputation-throttle.js +94 -0
- package/dist/governor/run-halt.d.ts +39 -0
- package/dist/governor/run-halt.js +83 -0
- package/dist/governor/self-plagiarism.d.ts +47 -0
- package/dist/governor/self-plagiarism.js +200 -0
- package/dist/governor/write-rate-limit.d.ts +52 -0
- package/dist/governor/write-rate-limit.js +132 -0
- package/dist/governor-ledger.d.ts +25 -0
- package/dist/governor-ledger.js +91 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +120 -0
- package/dist/issue-plan-decomposition.d.ts +18 -0
- package/dist/issue-plan-decomposition.js +56 -0
- package/dist/issue-rag-query.d.ts +12 -0
- package/dist/issue-rag-query.js +35 -0
- package/dist/local-scorer.d.ts +34 -0
- package/dist/local-scorer.js +36 -0
- package/dist/metadata-best-min-score.d.ts +9 -0
- package/dist/metadata-best-min-score.js +9 -0
- package/dist/metadata-best-pick.d.ts +9 -0
- package/dist/metadata-best-pick.js +9 -0
- package/dist/metadata-min-score.d.ts +9 -0
- package/dist/metadata-min-score.js +13 -0
- package/dist/metadata-top-min-score.d.ts +9 -0
- package/dist/metadata-top-min-score.js +13 -0
- package/dist/metadata-top-pick.d.ts +9 -0
- package/dist/metadata-top-pick.js +13 -0
- package/dist/miner/acceptance-criteria.d.ts +43 -0
- package/dist/miner/acceptance-criteria.js +77 -0
- package/dist/miner/agent-sdk-driver.d.ts +39 -0
- package/dist/miner/agent-sdk-driver.js +162 -0
- package/dist/miner/attempt-log.d.ts +29 -0
- package/dist/miner/attempt-log.js +100 -0
- package/dist/miner/attempt-metering.d.ts +39 -0
- package/dist/miner/attempt-metering.js +67 -0
- package/dist/miner/cli-subprocess-driver.d.ts +68 -0
- package/dist/miner/cli-subprocess-driver.js +272 -0
- package/dist/miner/coding-agent-driver.d.ts +33 -0
- package/dist/miner/coding-agent-driver.js +36 -0
- package/dist/miner/coding-agent-invoke.d.ts +11 -0
- package/dist/miner/coding-agent-invoke.js +80 -0
- package/dist/miner/coding-agent-mode.d.ts +25 -0
- package/dist/miner/coding-agent-mode.js +43 -0
- package/dist/miner/driver-factory.d.ts +82 -0
- package/dist/miner/driver-factory.js +169 -0
- package/dist/miner/harness-submission-trigger.d.ts +37 -0
- package/dist/miner/harness-submission-trigger.js +46 -0
- package/dist/miner/iterate-loop.d.ts +97 -0
- package/dist/miner/iterate-loop.js +278 -0
- package/dist/miner/iterate-policy.d.ts +98 -0
- package/dist/miner/iterate-policy.js +98 -0
- package/dist/miner/lint-guard.d.ts +50 -0
- package/dist/miner/lint-guard.js +68 -0
- package/dist/miner/local-write-tools.d.ts +81 -0
- package/dist/miner/local-write-tools.js +113 -0
- package/dist/miner/loop-reentry-policy.d.ts +36 -0
- package/dist/miner/loop-reentry-policy.js +53 -0
- package/dist/miner/repo-map.d.ts +51 -0
- package/dist/miner/repo-map.js +268 -0
- package/dist/miner/self-review-adapter.d.ts +106 -0
- package/dist/miner/self-review-adapter.js +83 -0
- package/dist/miner/submission-gate.d.ts +40 -0
- package/dist/miner/submission-gate.js +89 -0
- package/dist/miner/worktree-allocator.d.ts +59 -0
- package/dist/miner/worktree-allocator.js +60 -0
- package/dist/miner/worktree-pool.d.ts +55 -0
- package/dist/miner/worktree-pool.js +65 -0
- package/dist/miner-goal-lane-fit.d.ts +18 -0
- package/dist/miner-goal-lane-fit.js +76 -0
- package/dist/miner-goal-spec.d.ts +139 -0
- package/dist/miner-goal-spec.js +281 -0
- package/dist/miner-prediction-metrics.d.ts +16 -0
- package/dist/miner-prediction-metrics.js +61 -0
- package/dist/miner-telemetry.d.ts +32 -0
- package/dist/miner-telemetry.js +87 -0
- package/dist/objective-anchor.d.ts +125 -0
- package/dist/objective-anchor.js +401 -0
- package/dist/opportunity-competition.d.ts +6 -0
- package/dist/opportunity-competition.js +29 -0
- package/dist/opportunity-freshness.d.ts +18 -0
- package/dist/opportunity-freshness.js +55 -0
- package/dist/opportunity-metadata.d.ts +53 -0
- package/dist/opportunity-metadata.js +204 -0
- package/dist/opportunity-ranker.d.ts +46 -0
- package/dist/opportunity-ranker.js +74 -0
- package/dist/pairwise-calibration.d.ts +49 -0
- package/dist/pairwise-calibration.js +106 -0
- package/dist/phase7-calibration-loop.d.ts +136 -0
- package/dist/phase7-calibration-loop.js +437 -0
- package/dist/plan-blocked.d.ts +6 -0
- package/dist/plan-blocked.js +24 -0
- package/dist/plan-completed.d.ts +5 -0
- package/dist/plan-completed.js +6 -0
- package/dist/plan-completion.d.ts +6 -0
- package/dist/plan-completion.js +7 -0
- package/dist/plan-empty.d.ts +5 -0
- package/dist/plan-empty.js +6 -0
- package/dist/plan-export.d.ts +27 -0
- package/dist/plan-export.js +86 -0
- package/dist/plan-failure.d.ts +5 -0
- package/dist/plan-failure.js +6 -0
- package/dist/plan-overall-status.d.ts +6 -0
- package/dist/plan-overall-status.js +25 -0
- package/dist/plan-pending.d.ts +5 -0
- package/dist/plan-pending.js +6 -0
- package/dist/plan-progress-complete.d.ts +7 -0
- package/dist/plan-progress-complete.js +10 -0
- package/dist/plan-ready.d.ts +6 -0
- package/dist/plan-ready.js +12 -0
- package/dist/plan-running.d.ts +5 -0
- package/dist/plan-running.js +6 -0
- package/dist/plan-skipped.d.ts +5 -0
- package/dist/plan-skipped.js +6 -0
- package/dist/plan-step-count.d.ts +5 -0
- package/dist/plan-step-count.js +6 -0
- package/dist/plan-step-stats.d.ts +5 -0
- package/dist/plan-step-stats.js +6 -0
- package/dist/plan-templates.d.ts +25 -0
- package/dist/plan-templates.js +107 -0
- package/dist/plan-terminated.d.ts +6 -0
- package/dist/plan-terminated.js +9 -0
- package/dist/portfolio/non-convergence.d.ts +37 -0
- package/dist/portfolio/non-convergence.js +57 -0
- package/dist/portfolio/queue.d.ts +30 -0
- package/dist/portfolio/queue.js +150 -0
- package/dist/predicted-gate.d.ts +103 -0
- package/dist/predicted-gate.js +218 -0
- package/dist/prompt-packet.d.ts +11 -0
- package/dist/prompt-packet.js +34 -0
- package/dist/ranked-opportunity-best-min-score.d.ts +8 -0
- package/dist/ranked-opportunity-best-min-score.js +9 -0
- package/dist/ranked-opportunity-best-pick.d.ts +8 -0
- package/dist/ranked-opportunity-best-pick.js +9 -0
- package/dist/ranked-opportunity-min-score.d.ts +8 -0
- package/dist/ranked-opportunity-min-score.js +13 -0
- package/dist/ranked-opportunity-top-min-score.d.ts +8 -0
- package/dist/ranked-opportunity-top-min-score.js +13 -0
- package/dist/review/advisory-ai-routing-config.d.ts +8 -0
- package/dist/review/advisory-ai-routing-config.js +40 -0
- package/dist/review/check-names.d.ts +1 -0
- package/dist/review/check-names.js +1 -0
- package/dist/review/cla-check.d.ts +49 -0
- package/dist/review/cla-check.js +71 -0
- package/dist/review/diff-file-priority.d.ts +1 -0
- package/dist/review/diff-file-priority.js +12 -0
- package/dist/review/enrichment-analyzer-names.d.ts +3 -0
- package/dist/review/enrichment-analyzer-names.js +63 -0
- package/dist/review/guardrail-config.d.ts +10 -0
- package/dist/review/guardrail-config.js +62 -0
- package/dist/review/linked-issue-hard-rules-config.d.ts +4 -0
- package/dist/review/linked-issue-hard-rules-config.js +79 -0
- package/dist/review/linked-issue-label-propagation.d.ts +9 -0
- package/dist/review/linked-issue-label-propagation.js +114 -0
- package/dist/review/pre-merge-checks.d.ts +28 -0
- package/dist/review/pre-merge-checks.js +66 -0
- package/dist/review/review-thread-findings.d.ts +1 -0
- package/dist/review/review-thread-findings.js +1 -0
- package/dist/review/safe-url.d.ts +5 -0
- package/dist/review/safe-url.js +137 -0
- package/dist/review/screenshot-table-gate.d.ts +87 -0
- package/dist/review/screenshot-table-gate.js +340 -0
- package/dist/review/unlinked-issue-guardrail-config.d.ts +9 -0
- package/dist/review/unlinked-issue-guardrail-config.js +44 -0
- package/dist/reviewer-consensus-calibration.d.ts +113 -0
- package/dist/reviewer-consensus-calibration.js +493 -0
- package/dist/reward-risk.d.ts +223 -0
- package/dist/reward-risk.js +618 -0
- package/dist/scoring/label-match.d.ts +4 -0
- package/dist/scoring/label-match.js +145 -0
- package/dist/scoring/model.d.ts +31 -0
- package/dist/scoring/model.js +157 -0
- package/dist/scoring/pending-pr-scenarios.d.ts +44 -0
- package/dist/scoring/pending-pr-scenarios.js +145 -0
- package/dist/scoring/preview.d.ts +246 -0
- package/dist/scoring/preview.js +1108 -0
- package/dist/scoring/types.d.ts +131 -0
- package/dist/scoring/types.js +7 -0
- package/dist/settings/auto-close-exempt.d.ts +10 -0
- package/dist/settings/auto-close-exempt.js +58 -0
- package/dist/settings/autonomy.d.ts +33 -0
- package/dist/settings/autonomy.js +80 -0
- package/dist/settings/command-authorization.d.ts +49 -0
- package/dist/settings/command-authorization.js +222 -0
- package/dist/settings/contributor-blacklist.d.ts +17 -0
- package/dist/settings/contributor-blacklist.js +86 -0
- package/dist/settings/moderation-rules.d.ts +51 -0
- package/dist/settings/moderation-rules.js +104 -0
- package/dist/settings/pr-type-label.d.ts +70 -0
- package/dist/settings/pr-type-label.js +149 -0
- package/dist/signals/change-guardrail.d.ts +49 -0
- package/dist/signals/change-guardrail.js +164 -0
- package/dist/signals/check-summary.d.ts +9 -0
- package/dist/signals/check-summary.js +12 -0
- package/dist/signals/contributor-calibration.d.ts +23 -0
- package/dist/signals/contributor-calibration.js +47 -0
- package/dist/signals/duplicate-winner.d.ts +9 -0
- package/dist/signals/duplicate-winner.js +9 -0
- package/dist/signals/path-matchers.d.ts +36 -0
- package/dist/signals/path-matchers.js +371 -0
- package/dist/signals/predicted-gate-engine.d.ts +62 -0
- package/dist/signals/predicted-gate-engine.js +895 -0
- package/dist/signals/preflight-limits.d.ts +14 -0
- package/dist/signals/preflight-limits.js +14 -0
- package/dist/signals/slop.d.ts +56 -0
- package/dist/signals/slop.js +324 -0
- package/dist/signals/test-evidence.d.ts +25 -0
- package/dist/signals/test-evidence.js +108 -0
- package/dist/subprocess-env.d.ts +23 -0
- package/dist/subprocess-env.js +74 -0
- package/dist/track-record-summary.d.ts +92 -0
- package/dist/track-record-summary.js +301 -0
- package/dist/types/manifest-deps-types.d.ts +514 -0
- package/dist/types/manifest-deps-types.js +4 -0
- package/dist/types/predicted-gate-types.d.ts +338 -0
- package/dist/types/predicted-gate-types.js +3 -0
- package/dist/types/reward-risk-types.d.ts +319 -0
- package/dist/types/reward-risk-types.js +11 -0
- package/dist/utils/json.d.ts +1 -0
- package/dist/utils/json.js +3 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/package.json +80 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ObjectiveAnchorScore } from "./objective-anchor.js";
|
|
2
|
+
import type { PairwiseCalibrationScore } from "./pairwise-calibration.js";
|
|
3
|
+
export type GateVerdictCalibrationDimension = "correctness" | "tests" | "security" | "maintainability" | "scope" | "freshness" | "ci" | "policy";
|
|
4
|
+
export type GateVerdictCalibrationOutcome = "pass" | "warn" | "fail" | "unknown";
|
|
5
|
+
export type GateVerdictCalibrationManifest = {
|
|
6
|
+
miner?: {
|
|
7
|
+
calibration?: {
|
|
8
|
+
/** Explicit maintainer opt-in. Default false. */
|
|
9
|
+
shareStructuredGateVerdicts?: unknown;
|
|
10
|
+
/** Optional weight for the structured gate-verdict signal when composed into a replay score. */
|
|
11
|
+
structuredGateVerdictWeight?: unknown;
|
|
12
|
+
} | null;
|
|
13
|
+
} | null;
|
|
14
|
+
calibration?: {
|
|
15
|
+
/** Back-compat/future-friendly alias, still explicit and default-off. */
|
|
16
|
+
shareStructuredGateVerdicts?: unknown;
|
|
17
|
+
structuredGateVerdictWeight?: unknown;
|
|
18
|
+
} | null;
|
|
19
|
+
};
|
|
20
|
+
export type GateVerdictCalibrationConfig = {
|
|
21
|
+
shareStructuredGateVerdicts: boolean;
|
|
22
|
+
structuredGateVerdictWeight: number;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
};
|
|
25
|
+
export type GateVerdictCalibrationDimensionInput = {
|
|
26
|
+
dimension: GateVerdictCalibrationDimension | string;
|
|
27
|
+
outcome: GateVerdictCalibrationOutcome | string;
|
|
28
|
+
confidence?: number | undefined;
|
|
29
|
+
};
|
|
30
|
+
export type GateVerdictCalibrationSignalInput = {
|
|
31
|
+
repoFullName: string;
|
|
32
|
+
replayRunId: string;
|
|
33
|
+
gateRunId: string;
|
|
34
|
+
optedIn: boolean;
|
|
35
|
+
observedAt?: string | undefined;
|
|
36
|
+
dimensions: readonly GateVerdictCalibrationDimensionInput[];
|
|
37
|
+
};
|
|
38
|
+
export type GateVerdictCalibrationDimensionSignal = {
|
|
39
|
+
dimension: GateVerdictCalibrationDimension;
|
|
40
|
+
outcome: GateVerdictCalibrationOutcome;
|
|
41
|
+
confidence: number;
|
|
42
|
+
score: number;
|
|
43
|
+
};
|
|
44
|
+
export type GateVerdictCalibrationSignal = {
|
|
45
|
+
repoFullName: string;
|
|
46
|
+
replayRunId: string;
|
|
47
|
+
gateRunId: string;
|
|
48
|
+
observedAt: string | null;
|
|
49
|
+
dimensions: GateVerdictCalibrationDimensionSignal[];
|
|
50
|
+
score: number;
|
|
51
|
+
};
|
|
52
|
+
export type GateVerdictCalibrationIngestion = {
|
|
53
|
+
accepted: GateVerdictCalibrationSignal[];
|
|
54
|
+
rejected: Array<{
|
|
55
|
+
repoFullName: string;
|
|
56
|
+
replayRunId: string;
|
|
57
|
+
gateRunId: string;
|
|
58
|
+
reason: "not_opted_in" | "empty_dimensions" | "invalid_repo" | "invalid_run_id";
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
export type GateVerdictCalibrationWeights = {
|
|
62
|
+
objectiveAnchor?: number | undefined;
|
|
63
|
+
pairwiseJudge?: number | undefined;
|
|
64
|
+
structuredGateVerdict?: number | undefined;
|
|
65
|
+
};
|
|
66
|
+
export type GateVerdictCompositeCalibrationScore = {
|
|
67
|
+
compositeScore: number;
|
|
68
|
+
objectiveAnchorScore: number;
|
|
69
|
+
pairwiseJudgeScore: number | null;
|
|
70
|
+
structuredGateVerdictScore: number | null;
|
|
71
|
+
weights: {
|
|
72
|
+
objectiveAnchor: number;
|
|
73
|
+
pairwiseJudge: number;
|
|
74
|
+
structuredGateVerdict: number;
|
|
75
|
+
};
|
|
76
|
+
audit: {
|
|
77
|
+
contributingRepos: Array<{
|
|
78
|
+
repoFullName: string;
|
|
79
|
+
replayRunId: string;
|
|
80
|
+
gateRunId: string;
|
|
81
|
+
observedAt: string | null;
|
|
82
|
+
score: number;
|
|
83
|
+
dimensions: GateVerdictCalibrationDimensionSignal[];
|
|
84
|
+
}>;
|
|
85
|
+
rejected: GateVerdictCalibrationIngestion["rejected"];
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the explicit per-repo opt-in from a parsed `.gittensory.yml`-style object. Default is opted out. The
|
|
90
|
+
* preferred path is `miner.calibration.shareStructuredGateVerdicts`; `calibration.shareStructuredGateVerdicts` is
|
|
91
|
+
* accepted as a narrow alias so private-config surfaces can place the field at top level if needed.
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveGateVerdictCalibrationConfig(manifest: GateVerdictCalibrationManifest | Record<string, unknown> | null | undefined): GateVerdictCalibrationConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Ingest only currently opted-in structured gate-verdict signals. The opt-in check happens at ingestion time, so a
|
|
96
|
+
* maintainer opt-out immediately prevents additional calibration rows from contributing even if older collected data
|
|
97
|
+
* exists elsewhere.
|
|
98
|
+
*/
|
|
99
|
+
export declare function ingestGateVerdictCalibrationSignals(signals: readonly GateVerdictCalibrationSignalInput[]): GateVerdictCalibrationIngestion;
|
|
100
|
+
export declare function computeGateVerdictCompositeCalibrationScore(input: {
|
|
101
|
+
objectiveAnchor: number | ObjectiveAnchorScore;
|
|
102
|
+
pairwise: number | PairwiseCalibrationScore | null;
|
|
103
|
+
gateVerdicts: GateVerdictCalibrationIngestion | readonly GateVerdictCalibrationSignalInput[];
|
|
104
|
+
weights?: GateVerdictCalibrationWeights | undefined;
|
|
105
|
+
}): GateVerdictCompositeCalibrationScore;
|
|
106
|
+
/**
|
|
107
|
+
* Render a deterministic, public-safe Markdown report for a structured gate-verdict calibration result. The report is
|
|
108
|
+
* local-run evidence: it includes aggregate scores, normalized weights, opted-in contributors, and rejected rows, but
|
|
109
|
+
* never accepts or emits raw review text or private scoring fields.
|
|
110
|
+
*/
|
|
111
|
+
export declare function renderGateVerdictCalibrationAuditMarkdown(result: GateVerdictCompositeCalibrationScore): string;
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
// Opt-in structured gate-verdict calibration signal (#3015).
|
|
2
|
+
//
|
|
3
|
+
// This module is the pure engine half of cross-product calibration. The hosted review stack can decide whether a
|
|
4
|
+
// repo is currently opted in from its resolved `.gittensory.yml`/private config; the miner replay harness can then
|
|
5
|
+
// ingest only the structured per-dimension verdict fields exposed here. No raw review text, secrets, trust values,
|
|
6
|
+
// rewards, rankings, or maintainer evidence are represented in this type surface.
|
|
7
|
+
const DIMENSION_ORDER = [
|
|
8
|
+
"correctness",
|
|
9
|
+
"tests",
|
|
10
|
+
"security",
|
|
11
|
+
"maintainability",
|
|
12
|
+
"scope",
|
|
13
|
+
"freshness",
|
|
14
|
+
"ci",
|
|
15
|
+
"policy",
|
|
16
|
+
];
|
|
17
|
+
const OUTCOME_SCORE = {
|
|
18
|
+
pass: 1,
|
|
19
|
+
warn: 0.5,
|
|
20
|
+
fail: 0,
|
|
21
|
+
unknown: 0,
|
|
22
|
+
};
|
|
23
|
+
const DEFAULT_STRUCTURED_GATE_WEIGHT = 0.2;
|
|
24
|
+
const DEFAULT_COMPOSITE_WEIGHTS = {
|
|
25
|
+
objectiveAnchor: 0.45,
|
|
26
|
+
pairwiseJudge: 0.35,
|
|
27
|
+
structuredGateVerdict: 0.2,
|
|
28
|
+
};
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
31
|
+
}
|
|
32
|
+
function finiteNonNegative(value, fallback) {
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
return fallback;
|
|
35
|
+
if (!Number.isFinite(value) || value < 0)
|
|
36
|
+
return 0;
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function roundScore(value) {
|
|
40
|
+
return Math.round(Math.min(1, Math.max(0, value)) * 1_000_000) / 1_000_000;
|
|
41
|
+
}
|
|
42
|
+
function normalizeRepoFullName(value) {
|
|
43
|
+
const trimmed = value.trim().toLowerCase();
|
|
44
|
+
if (!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/u.test(trimmed))
|
|
45
|
+
return null;
|
|
46
|
+
return trimmed;
|
|
47
|
+
}
|
|
48
|
+
function normalizeId(value) {
|
|
49
|
+
const trimmed = value.trim();
|
|
50
|
+
if (!trimmed || trimmed.length > 160 || /[\r\n\0]/u.test(trimmed))
|
|
51
|
+
return null;
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
function normalizeObservedAt(value) {
|
|
55
|
+
if (!value)
|
|
56
|
+
return null;
|
|
57
|
+
const ms = Date.parse(value);
|
|
58
|
+
if (!Number.isFinite(ms))
|
|
59
|
+
return null;
|
|
60
|
+
return new Date(ms).toISOString();
|
|
61
|
+
}
|
|
62
|
+
function normalizeBoolean(value) {
|
|
63
|
+
if (typeof value === "boolean")
|
|
64
|
+
return value;
|
|
65
|
+
if (typeof value !== "string")
|
|
66
|
+
return undefined;
|
|
67
|
+
const normalized = value.trim().toLowerCase();
|
|
68
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
69
|
+
return true;
|
|
70
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
71
|
+
return false;
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
function normalizeOptionalWeight(value) {
|
|
75
|
+
if (value === undefined || value === null)
|
|
76
|
+
return undefined;
|
|
77
|
+
const number = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : Number.NaN;
|
|
78
|
+
if (!Number.isFinite(number) || number < 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
return number;
|
|
81
|
+
}
|
|
82
|
+
function normalizeDimension(value) {
|
|
83
|
+
const normalized = value.trim().toLowerCase().replace(/[_\s-]+/gu, "_");
|
|
84
|
+
if (normalized === "quality" || normalized === "code_quality")
|
|
85
|
+
return "correctness";
|
|
86
|
+
if (normalized === "test" || normalized === "coverage")
|
|
87
|
+
return "tests";
|
|
88
|
+
if (normalized === "maintainability" || normalized === "maintenance")
|
|
89
|
+
return "maintainability";
|
|
90
|
+
if (normalized === "size" || normalized === "blast_radius")
|
|
91
|
+
return "scope";
|
|
92
|
+
if (normalized === "rebase" || normalized === "up_to_date")
|
|
93
|
+
return "freshness";
|
|
94
|
+
if (normalized === "workflow" || normalized === "checks")
|
|
95
|
+
return "ci";
|
|
96
|
+
if (DIMENSION_ORDER.includes(normalized))
|
|
97
|
+
return normalized;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function normalizeOutcome(value) {
|
|
101
|
+
const normalized = value.trim().toLowerCase().replace(/[_\s-]+/gu, "_");
|
|
102
|
+
if (normalized === "ok" || normalized === "success" || normalized === "passed")
|
|
103
|
+
return "pass";
|
|
104
|
+
if (normalized === "warning" || normalized === "advisory" || normalized === "hold")
|
|
105
|
+
return "warn";
|
|
106
|
+
if (normalized === "block" || normalized === "blocked" || normalized === "failed")
|
|
107
|
+
return "fail";
|
|
108
|
+
if (["pass", "warn", "fail", "unknown"].includes(normalized)) {
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function clampConfidence(value) {
|
|
114
|
+
if (value === undefined)
|
|
115
|
+
return 1;
|
|
116
|
+
if (!Number.isFinite(value))
|
|
117
|
+
return 0;
|
|
118
|
+
return Math.min(1, Math.max(0, value));
|
|
119
|
+
}
|
|
120
|
+
function normalizeDimensions(dimensions) {
|
|
121
|
+
const byDimension = new Map();
|
|
122
|
+
for (const item of dimensions) {
|
|
123
|
+
const dimension = normalizeDimension(item.dimension);
|
|
124
|
+
const outcome = normalizeOutcome(item.outcome);
|
|
125
|
+
if (!dimension || !outcome)
|
|
126
|
+
continue;
|
|
127
|
+
const confidence = clampConfidence(item.confidence);
|
|
128
|
+
const score = roundScore(OUTCOME_SCORE[outcome] * confidence);
|
|
129
|
+
const existing = byDimension.get(dimension);
|
|
130
|
+
if (!existing || score < existing.score) {
|
|
131
|
+
byDimension.set(dimension, { dimension, outcome, confidence, score });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return DIMENSION_ORDER.flatMap((dimension) => {
|
|
135
|
+
const signal = byDimension.get(dimension);
|
|
136
|
+
return signal ? [signal] : [];
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function averageSignals(signals) {
|
|
140
|
+
if (signals.length === 0)
|
|
141
|
+
return null;
|
|
142
|
+
return roundScore(signals.reduce((sum, signal) => sum + signal.score, 0) / signals.length);
|
|
143
|
+
}
|
|
144
|
+
function isGateVerdictCalibrationIngestion(value) {
|
|
145
|
+
return isRecord(value) && Array.isArray(value.accepted) && Array.isArray(value.rejected);
|
|
146
|
+
}
|
|
147
|
+
function sanitizeGateVerdictCalibrationIngestion(ingestion) {
|
|
148
|
+
const accepted = [];
|
|
149
|
+
const rejected = [];
|
|
150
|
+
for (const signal of ingestion.accepted) {
|
|
151
|
+
if (!isRecord(signal) || !Array.isArray(signal.dimensions))
|
|
152
|
+
continue;
|
|
153
|
+
const repoFullName = typeof signal.repoFullName === "string" ? normalizeRepoFullName(signal.repoFullName) : null;
|
|
154
|
+
const replayRunId = typeof signal.replayRunId === "string" ? normalizeId(signal.replayRunId) : null;
|
|
155
|
+
const gateRunId = typeof signal.gateRunId === "string" ? normalizeId(signal.gateRunId) : null;
|
|
156
|
+
if (!repoFullName || !replayRunId || !gateRunId)
|
|
157
|
+
continue;
|
|
158
|
+
const dimensionInputs = signal.dimensions.flatMap((dimension) => {
|
|
159
|
+
if (!isRecord(dimension) || typeof dimension.dimension !== "string" || typeof dimension.outcome !== "string") {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
dimension: dimension.dimension,
|
|
165
|
+
outcome: dimension.outcome,
|
|
166
|
+
confidence: typeof dimension.confidence === "number" ? dimension.confidence : undefined,
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
});
|
|
170
|
+
const dimensions = normalizeDimensions(dimensionInputs);
|
|
171
|
+
if (dimensions.length === 0)
|
|
172
|
+
continue;
|
|
173
|
+
accepted.push({
|
|
174
|
+
repoFullName,
|
|
175
|
+
replayRunId,
|
|
176
|
+
gateRunId,
|
|
177
|
+
observedAt: typeof signal.observedAt === "string" ? normalizeObservedAt(signal.observedAt) : null,
|
|
178
|
+
dimensions,
|
|
179
|
+
score: roundScore(dimensions.reduce((sum, item) => sum + item.score, 0) / dimensions.length),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
for (const row of ingestion.rejected) {
|
|
183
|
+
if (!isRecord(row))
|
|
184
|
+
continue;
|
|
185
|
+
const repoFullName = typeof row.repoFullName === "string" ? normalizeRepoFullName(row.repoFullName) : null;
|
|
186
|
+
const replayRunId = typeof row.replayRunId === "string" ? normalizeId(row.replayRunId) : null;
|
|
187
|
+
const gateRunId = typeof row.gateRunId === "string" ? normalizeId(row.gateRunId) : null;
|
|
188
|
+
const reason = row.reason;
|
|
189
|
+
if (!repoFullName ||
|
|
190
|
+
!replayRunId ||
|
|
191
|
+
!gateRunId ||
|
|
192
|
+
!["not_opted_in", "empty_dimensions", "invalid_repo", "invalid_run_id"].includes(reason)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
rejected.push({ repoFullName, replayRunId, gateRunId, reason });
|
|
196
|
+
}
|
|
197
|
+
return { accepted, rejected };
|
|
198
|
+
}
|
|
199
|
+
function normalizeCompositeWeights(weights) {
|
|
200
|
+
const raw = {
|
|
201
|
+
objectiveAnchor: finiteNonNegative(weights?.objectiveAnchor, DEFAULT_COMPOSITE_WEIGHTS.objectiveAnchor),
|
|
202
|
+
pairwiseJudge: finiteNonNegative(weights?.pairwiseJudge, DEFAULT_COMPOSITE_WEIGHTS.pairwiseJudge),
|
|
203
|
+
structuredGateVerdict: finiteNonNegative(weights?.structuredGateVerdict, DEFAULT_COMPOSITE_WEIGHTS.structuredGateVerdict),
|
|
204
|
+
};
|
|
205
|
+
const total = raw.objectiveAnchor + raw.pairwiseJudge + raw.structuredGateVerdict;
|
|
206
|
+
if (total <= 0)
|
|
207
|
+
return DEFAULT_COMPOSITE_WEIGHTS;
|
|
208
|
+
return {
|
|
209
|
+
objectiveAnchor: raw.objectiveAnchor / total,
|
|
210
|
+
pairwiseJudge: raw.pairwiseJudge / total,
|
|
211
|
+
structuredGateVerdict: raw.structuredGateVerdict / total,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function markdownSafe(value) {
|
|
215
|
+
return value.replace(/[\r\n]+/gu, " ").replace(/[\\`*_[\]<>|]/gu, "\\$&");
|
|
216
|
+
}
|
|
217
|
+
function markdownList(values) {
|
|
218
|
+
if (values.length === 0)
|
|
219
|
+
return "- none";
|
|
220
|
+
return values.map((value) => `- ${markdownSafe(value)}`).join("\n");
|
|
221
|
+
}
|
|
222
|
+
function renderDimensionRows(dimensions) {
|
|
223
|
+
if (dimensions.length === 0)
|
|
224
|
+
return "| Dimension | Outcome | Confidence | Score |\n| --- | --- | ---: | ---: |\n";
|
|
225
|
+
return [
|
|
226
|
+
"| Dimension | Outcome | Confidence | Score |",
|
|
227
|
+
"| --- | --- | ---: | ---: |",
|
|
228
|
+
...dimensions.map((dimension) => `| ${markdownSafe(dimension.dimension)} | ${markdownSafe(dimension.outcome)} | ${dimension.confidence.toFixed(6)} | ${dimension.score.toFixed(6)} |`),
|
|
229
|
+
].join("\n");
|
|
230
|
+
}
|
|
231
|
+
function renderContributingRepo(signal) {
|
|
232
|
+
return [
|
|
233
|
+
`### ${markdownSafe(signal.repoFullName)}`,
|
|
234
|
+
"",
|
|
235
|
+
`- replayRunId: ${markdownSafe(signal.replayRunId)}`,
|
|
236
|
+
`- gateRunId: ${markdownSafe(signal.gateRunId)}`,
|
|
237
|
+
`- observedAt: ${signal.observedAt ? markdownSafe(signal.observedAt) : "n/a"}`,
|
|
238
|
+
`- score: ${signal.score.toFixed(6)}`,
|
|
239
|
+
"",
|
|
240
|
+
renderDimensionRows(signal.dimensions),
|
|
241
|
+
].join("\n");
|
|
242
|
+
}
|
|
243
|
+
function renderRejectedRow(row) {
|
|
244
|
+
return `| ${markdownSafe(row.repoFullName)} | ${markdownSafe(row.replayRunId)} | ${markdownSafe(row.gateRunId)} | ${markdownSafe(row.reason)} |`;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Resolve the explicit per-repo opt-in from a parsed `.gittensory.yml`-style object. Default is opted out. The
|
|
248
|
+
* preferred path is `miner.calibration.shareStructuredGateVerdicts`; `calibration.shareStructuredGateVerdicts` is
|
|
249
|
+
* accepted as a narrow alias so private-config surfaces can place the field at top level if needed.
|
|
250
|
+
*/
|
|
251
|
+
export function resolveGateVerdictCalibrationConfig(manifest) {
|
|
252
|
+
const warnings = [];
|
|
253
|
+
const root = isRecord(manifest) ? manifest : {};
|
|
254
|
+
const miner = isRecord(root.miner) ? root.miner : {};
|
|
255
|
+
const minerCalibration = isRecord(miner.calibration) ? miner.calibration : {};
|
|
256
|
+
const topCalibration = isRecord(root.calibration) ? root.calibration : {};
|
|
257
|
+
const optInRaw = minerCalibration.shareStructuredGateVerdicts ?? topCalibration.shareStructuredGateVerdicts ?? undefined;
|
|
258
|
+
const optIn = normalizeBoolean(optInRaw);
|
|
259
|
+
if (optInRaw !== undefined && optIn === undefined) {
|
|
260
|
+
warnings.push("miner.calibration.shareStructuredGateVerdicts must be a boolean-like value; defaulting to false.");
|
|
261
|
+
}
|
|
262
|
+
const weightRaw = minerCalibration.structuredGateVerdictWeight ?? topCalibration.structuredGateVerdictWeight;
|
|
263
|
+
const weight = normalizeOptionalWeight(weightRaw);
|
|
264
|
+
if (weightRaw !== undefined && weight === undefined) {
|
|
265
|
+
warnings.push("miner.calibration.structuredGateVerdictWeight must be a non-negative finite number; using default.");
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
shareStructuredGateVerdicts: optIn === true,
|
|
269
|
+
structuredGateVerdictWeight: weight ?? DEFAULT_STRUCTURED_GATE_WEIGHT,
|
|
270
|
+
warnings,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Ingest only currently opted-in structured gate-verdict signals. The opt-in check happens at ingestion time, so a
|
|
275
|
+
* maintainer opt-out immediately prevents additional calibration rows from contributing even if older collected data
|
|
276
|
+
* exists elsewhere.
|
|
277
|
+
*/
|
|
278
|
+
export function ingestGateVerdictCalibrationSignals(signals) {
|
|
279
|
+
const accepted = [];
|
|
280
|
+
const rejected = [];
|
|
281
|
+
for (const signal of signals) {
|
|
282
|
+
const repoFullName = normalizeRepoFullName(signal.repoFullName);
|
|
283
|
+
const replayRunId = normalizeId(signal.replayRunId);
|
|
284
|
+
const gateRunId = normalizeId(signal.gateRunId);
|
|
285
|
+
if (!repoFullName) {
|
|
286
|
+
rejected.push({
|
|
287
|
+
repoFullName: signal.repoFullName,
|
|
288
|
+
replayRunId: signal.replayRunId,
|
|
289
|
+
gateRunId: signal.gateRunId,
|
|
290
|
+
reason: "invalid_repo",
|
|
291
|
+
});
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (!replayRunId || !gateRunId) {
|
|
295
|
+
rejected.push({
|
|
296
|
+
repoFullName,
|
|
297
|
+
replayRunId: signal.replayRunId,
|
|
298
|
+
gateRunId: signal.gateRunId,
|
|
299
|
+
reason: "invalid_run_id",
|
|
300
|
+
});
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!signal.optedIn) {
|
|
304
|
+
rejected.push({ repoFullName, replayRunId, gateRunId, reason: "not_opted_in" });
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const dimensions = normalizeDimensions(signal.dimensions);
|
|
308
|
+
if (dimensions.length === 0) {
|
|
309
|
+
rejected.push({ repoFullName, replayRunId, gateRunId, reason: "empty_dimensions" });
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
accepted.push({
|
|
313
|
+
repoFullName,
|
|
314
|
+
replayRunId,
|
|
315
|
+
gateRunId,
|
|
316
|
+
observedAt: normalizeObservedAt(signal.observedAt),
|
|
317
|
+
dimensions,
|
|
318
|
+
score: roundScore(dimensions.reduce((sum, item) => sum + item.score, 0) / dimensions.length),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
return { accepted, rejected };
|
|
322
|
+
}
|
|
323
|
+
export function computeGateVerdictCompositeCalibrationScore(input) {
|
|
324
|
+
const ingestion = isGateVerdictCalibrationIngestion(input.gateVerdicts)
|
|
325
|
+
? sanitizeGateVerdictCalibrationIngestion(input.gateVerdicts)
|
|
326
|
+
: ingestGateVerdictCalibrationSignals(input.gateVerdicts);
|
|
327
|
+
const objectiveAnchorScore = typeof input.objectiveAnchor === "number" ? roundScore(input.objectiveAnchor) : input.objectiveAnchor.score;
|
|
328
|
+
const pairwiseJudgeScore = input.pairwise === null
|
|
329
|
+
? null
|
|
330
|
+
: typeof input.pairwise === "number"
|
|
331
|
+
? roundScore(input.pairwise)
|
|
332
|
+
: input.pairwise.pairwiseJudgeScore;
|
|
333
|
+
const structuredGateVerdictScore = averageSignals(ingestion.accepted);
|
|
334
|
+
const rawWeights = normalizeCompositeWeights(input.weights);
|
|
335
|
+
const usableWeights = {
|
|
336
|
+
objectiveAnchor: rawWeights.objectiveAnchor,
|
|
337
|
+
pairwiseJudge: pairwiseJudgeScore === null ? 0 : rawWeights.pairwiseJudge,
|
|
338
|
+
structuredGateVerdict: structuredGateVerdictScore === null ? 0 : rawWeights.structuredGateVerdict,
|
|
339
|
+
};
|
|
340
|
+
const total = usableWeights.objectiveAnchor + usableWeights.pairwiseJudge + usableWeights.structuredGateVerdict;
|
|
341
|
+
const weights = total <= 0
|
|
342
|
+
? { objectiveAnchor: 1, pairwiseJudge: 0, structuredGateVerdict: 0 }
|
|
343
|
+
: {
|
|
344
|
+
objectiveAnchor: usableWeights.objectiveAnchor / total,
|
|
345
|
+
pairwiseJudge: usableWeights.pairwiseJudge / total,
|
|
346
|
+
structuredGateVerdict: usableWeights.structuredGateVerdict / total,
|
|
347
|
+
};
|
|
348
|
+
const compositeScore = roundScore(objectiveAnchorScore * weights.objectiveAnchor +
|
|
349
|
+
(pairwiseJudgeScore ?? 0) * weights.pairwiseJudge +
|
|
350
|
+
(structuredGateVerdictScore ?? 0) * weights.structuredGateVerdict);
|
|
351
|
+
return {
|
|
352
|
+
compositeScore,
|
|
353
|
+
objectiveAnchorScore,
|
|
354
|
+
pairwiseJudgeScore,
|
|
355
|
+
structuredGateVerdictScore,
|
|
356
|
+
weights,
|
|
357
|
+
audit: {
|
|
358
|
+
contributingRepos: ingestion.accepted.map((signal) => ({
|
|
359
|
+
repoFullName: signal.repoFullName,
|
|
360
|
+
replayRunId: signal.replayRunId,
|
|
361
|
+
gateRunId: signal.gateRunId,
|
|
362
|
+
observedAt: signal.observedAt,
|
|
363
|
+
score: signal.score,
|
|
364
|
+
dimensions: signal.dimensions,
|
|
365
|
+
})),
|
|
366
|
+
rejected: ingestion.rejected,
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Render a deterministic, public-safe Markdown report for a structured gate-verdict calibration result. The report is
|
|
372
|
+
* local-run evidence: it includes aggregate scores, normalized weights, opted-in contributors, and rejected rows, but
|
|
373
|
+
* never accepts or emits raw review text or private scoring fields.
|
|
374
|
+
*/
|
|
375
|
+
export function renderGateVerdictCalibrationAuditMarkdown(result) {
|
|
376
|
+
const lines = [
|
|
377
|
+
"# Structured Gate-Verdict Calibration",
|
|
378
|
+
"",
|
|
379
|
+
`Composite score: ${result.compositeScore.toFixed(6)}`,
|
|
380
|
+
"",
|
|
381
|
+
"## Component Scores",
|
|
382
|
+
"",
|
|
383
|
+
`- objectiveAnchor: ${result.objectiveAnchorScore.toFixed(6)}`,
|
|
384
|
+
`- pairwiseJudge: ${result.pairwiseJudgeScore === null ? "n/a" : result.pairwiseJudgeScore.toFixed(6)}`,
|
|
385
|
+
`- structuredGateVerdict: ${result.structuredGateVerdictScore === null ? "n/a" : result.structuredGateVerdictScore.toFixed(6)}`,
|
|
386
|
+
"",
|
|
387
|
+
"## Effective Weights",
|
|
388
|
+
"",
|
|
389
|
+
`- objectiveAnchor: ${result.weights.objectiveAnchor.toFixed(6)}`,
|
|
390
|
+
`- pairwiseJudge: ${result.weights.pairwiseJudge.toFixed(6)}`,
|
|
391
|
+
`- structuredGateVerdict: ${result.weights.structuredGateVerdict.toFixed(6)}`,
|
|
392
|
+
"",
|
|
393
|
+
"## Contributing Repos",
|
|
394
|
+
"",
|
|
395
|
+
result.audit.contributingRepos.length === 0
|
|
396
|
+
? "_No opted-in structured gate-verdict signals contributed._"
|
|
397
|
+
: result.audit.contributingRepos.map(renderContributingRepo).join("\n\n"),
|
|
398
|
+
"",
|
|
399
|
+
"## Rejected Rows",
|
|
400
|
+
"",
|
|
401
|
+
];
|
|
402
|
+
if (result.audit.rejected.length === 0) {
|
|
403
|
+
lines.push("- none");
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
lines.push("| Repo | Replay run | Gate run | Reason |", "| --- | --- | --- | --- |", ...result.audit.rejected.map(renderRejectedRow));
|
|
407
|
+
}
|
|
408
|
+
const contributingRepos = result.audit.contributingRepos.map((repo) => repo.repoFullName);
|
|
409
|
+
lines.push("", "## Contributing Repo Summary", "", markdownList(contributingRepos));
|
|
410
|
+
return `${lines.join("\n")}\n`;
|
|
411
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const GITTENSOR_HOME_URL = "https://gittensor.io";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const GITTENSOR_HOME_URL = "https://gittensor.io";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sanitizePublicComment(value: string): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function sanitizePublicComment(value) {
|
|
2
|
+
const sanitized = value
|
|
3
|
+
.replace(/\bopen pr count\s+\d+\s+exceeds threshold\s+\d+\b\.?/gi, "private context")
|
|
4
|
+
.replace(/\bopen pr count is at or below\s+\d+\b/gi, "private context")
|
|
5
|
+
.replace(/\bmerged pr count\s+\d+\s+is below upstream floor\s+\d+\b\.?/gi, "private context")
|
|
6
|
+
.replace(/\bissue-discovery history\s*\(\s*\d+\s+valid solved,\s*credibility\s+[-+]?\d+(?:\.\d+)?\s*\)\s+is below upstream floors\s*\(\s*\d+\s+valid solved,\s*[-+]?\d+(?:\.\d+)?\s+credibility\s*\)\.?/gi, "private context")
|
|
7
|
+
.replace(/\bcredibility\s+[-+]?\d+(?:\.\d+)?\s+is below floor\s+[-+]?\d+(?:\.\d+)?\b\.?/gi, "private context")
|
|
8
|
+
.replace(/\b(?:effective|projected|estimated) score(?: changes?)?\b(?:\s+from)?\s+[-+]?\d+(?:\.\d+)?\s*(?:->|→|to)\s*[-+]?\d+(?:\.\d+)?/gi, "private context")
|
|
9
|
+
.replace(/\b(raw trust scores?|trust scores?|wallets?|hotkeys?|coldkeys?|seed phrases?|mnemonics?)\b/gi, "private context")
|
|
10
|
+
.replace(/\b(public score estimates?|estimated scores?|score estimates?|estimated rewards?|rewards?|reward estimates?|payouts?|farming|scoreability|score previews?|projected score changes?)\b/gi, "private context")
|
|
11
|
+
.replace(/\b(private reviewability|reviewability internals?)\b/gi, "private context")
|
|
12
|
+
.replace(/\b(private rankings?|rankings?)\b/gi, "private context")
|
|
13
|
+
.replace(/\b(?:open_pr_pressure|closed_pr_credibility|low_credibility|maintainer_lane|inactive_or_unknown_lane|issue_discovery_only|merged_pr_history_floor|issue_discovery_validity_floor)\b/gi, "private context")
|
|
14
|
+
.replace(/\b(?:credibility(?: updates?)?|closed pr credibility|low credibility|open pr pressure)\b/gi, "private context")
|
|
15
|
+
// Catch-all: a phrase replacement above (e.g. "score estimate"/"score preview") can leave a bare
|
|
16
|
+
// numeric score transition behind ("private context 32.5 -> 41.2"); redact those residual numbers too.
|
|
17
|
+
.replace(/\bprivate context\b\s+[-+]?\d+(?:\.\d+)?\s*(?:->|→|to)\s*[-+]?\d+(?:\.\d+)?/gi, "private context")
|
|
18
|
+
.replace(/\blikely_duplicate\b/gi, "possible overlap with existing work");
|
|
19
|
+
return sanitizeReviewabilityTerm(sanitized).replace(/private context(?:,\s*private context)+/gi, "private context");
|
|
20
|
+
}
|
|
21
|
+
function sanitizeReviewabilityTerm(value) {
|
|
22
|
+
return value.replace(/\breviewability\b/gi, (match, offset, fullText) => {
|
|
23
|
+
const prefix = fullText.slice(Math.max(0, offset - "@gittensory ".length), offset).toLowerCase();
|
|
24
|
+
return prefix.endsWith("@gittensory ") ? match : "private context";
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
function normalizeLabels(labels) {
|
|
2
|
+
return labels
|
|
3
|
+
.filter((label) => typeof label === "string")
|
|
4
|
+
.map((label) => label.trim().toLowerCase())
|
|
5
|
+
.filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
function normalizePathForMatch(path) {
|
|
8
|
+
return String(path ?? "").replace(/\\/g, "/").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function compileGlobMatcher(pattern) {
|
|
11
|
+
const normalizedPattern = normalizePathForMatch(pattern);
|
|
12
|
+
if (!normalizedPattern)
|
|
13
|
+
return () => false;
|
|
14
|
+
let regex = "^";
|
|
15
|
+
for (let i = 0; i < normalizedPattern.length; i++) {
|
|
16
|
+
const ch = normalizedPattern[i];
|
|
17
|
+
const next = normalizedPattern[i + 1];
|
|
18
|
+
if (ch === "*" && next === "*") {
|
|
19
|
+
const afterDoubleStar = normalizedPattern[i + 2];
|
|
20
|
+
if (afterDoubleStar === "/") {
|
|
21
|
+
regex += "(?:.*/)?";
|
|
22
|
+
i += 2;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
regex += ".*";
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (ch === "*") {
|
|
30
|
+
regex += "[^/]*";
|
|
31
|
+
}
|
|
32
|
+
else if (ch === "?") {
|
|
33
|
+
regex += "[^/]";
|
|
34
|
+
}
|
|
35
|
+
else if (/[.+^$(){}|[\]\\]/.test(ch ?? "")) {
|
|
36
|
+
regex += "\\" + ch;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
regex += ch;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
regex += "$";
|
|
43
|
+
const compiled = new RegExp(regex);
|
|
44
|
+
return (path) => {
|
|
45
|
+
const normalized = normalizePathForMatch(path);
|
|
46
|
+
if (!normalized)
|
|
47
|
+
return false;
|
|
48
|
+
return compiled.test(normalized);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function matchesAnyLabel(candidateLabels, goalLabels) {
|
|
52
|
+
if (goalLabels.length === 0)
|
|
53
|
+
return false;
|
|
54
|
+
const normalizedCandidate = normalizeLabels(candidateLabels);
|
|
55
|
+
const normalizedGoal = normalizeLabels(goalLabels);
|
|
56
|
+
return normalizedGoal.some((label) => normalizedCandidate.includes(label));
|
|
57
|
+
}
|
|
58
|
+
function matchesAnyPath(candidatePaths, goalPaths) {
|
|
59
|
+
if (goalPaths.length === 0)
|
|
60
|
+
return false;
|
|
61
|
+
return goalPaths.some((pattern) => {
|
|
62
|
+
const matcher = compileGlobMatcher(pattern);
|
|
63
|
+
return candidatePaths.some((path) => matcher(path));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export function computeLaneFit(input) {
|
|
67
|
+
const { candidatePaths, candidateLabels, goalSpec } = input;
|
|
68
|
+
if (matchesAnyPath(candidatePaths, goalSpec.blockedPaths)) {
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
if (matchesAnyLabel(candidateLabels, goalSpec.blockedLabels)) {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
const hasPathCriteria = goalSpec.wantedPaths.length > 0;
|
|
75
|
+
const hasLabelCriteria = goalSpec.preferredLabels.length > 0;
|
|
76
|
+
if (!hasPathCriteria && !hasLabelCriteria) {
|
|
77
|
+
return 0.5;
|
|
78
|
+
}
|
|
79
|
+
const pathMatches = hasPathCriteria && matchesAnyPath(candidatePaths, goalSpec.wantedPaths);
|
|
80
|
+
const labelMatches = hasLabelCriteria && matchesAnyLabel(candidateLabels, goalSpec.preferredLabels);
|
|
81
|
+
if (!pathMatches && !labelMatches) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const activeDimensions = (hasPathCriteria ? 1 : 0) + (hasLabelCriteria ? 1 : 0);
|
|
85
|
+
const matchedDimensions = (pathMatches ? 1 : 0) + (labelMatches ? 1 : 0);
|
|
86
|
+
return matchedDimensions / activeDimensions;
|
|
87
|
+
}
|