@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,136 @@
|
|
|
1
|
+
import type { GateVerdictCompositeCalibrationScore } from "./gate-verdict-calibration.js";
|
|
2
|
+
/** Documented self-review calibration baseline from the Phase 7 roadmap (#2994). */
|
|
3
|
+
export declare const DOCUMENTED_CALIBRATION_BASELINE = 0.62;
|
|
4
|
+
export type CalibrationSignalSource = "historical_replay" | "pr_outcome";
|
|
5
|
+
export type ReplayHarnessStatus = "healthy" | "degraded" | "unavailable";
|
|
6
|
+
export type Phase7CalibrationManifest = {
|
|
7
|
+
miner?: {
|
|
8
|
+
calibration?: {
|
|
9
|
+
/** Explicit opt-in for Phase 7 loop gating. Default false. */
|
|
10
|
+
phase7LoopEnabled?: unknown;
|
|
11
|
+
/** Combined calibration accuracy required before any autonomy-level increase. Default 0.70. */
|
|
12
|
+
autonomyIncreaseMinAccuracy?: unknown;
|
|
13
|
+
/** Maximum replay-run age before the harness is treated as stale. Default 168 hours. */
|
|
14
|
+
replayFreshnessMaxAgeHours?: unknown;
|
|
15
|
+
/** Weight for the historical-replay composite signal when composing the tracked metric. Default 0.5. */
|
|
16
|
+
historicalReplayWeight?: unknown;
|
|
17
|
+
/** Weight for the live pr_outcome signal when composing the tracked metric. Default 0.5. */
|
|
18
|
+
prOutcomeWeight?: unknown;
|
|
19
|
+
/** Minimum decided pr_outcome samples before the live signal contributes. Default 10. */
|
|
20
|
+
prOutcomeMinDecided?: unknown;
|
|
21
|
+
} | null;
|
|
22
|
+
} | null;
|
|
23
|
+
calibration?: {
|
|
24
|
+
phase7LoopEnabled?: unknown;
|
|
25
|
+
autonomyIncreaseMinAccuracy?: unknown;
|
|
26
|
+
replayFreshnessMaxAgeHours?: unknown;
|
|
27
|
+
historicalReplayWeight?: unknown;
|
|
28
|
+
prOutcomeWeight?: unknown;
|
|
29
|
+
prOutcomeMinDecided?: unknown;
|
|
30
|
+
} | null;
|
|
31
|
+
};
|
|
32
|
+
export type Phase7CalibrationConfig = {
|
|
33
|
+
phase7LoopEnabled: boolean;
|
|
34
|
+
autonomyIncreaseMinAccuracy: number;
|
|
35
|
+
replayFreshnessMaxAgeHours: number;
|
|
36
|
+
historicalReplayWeight: number;
|
|
37
|
+
prOutcomeWeight: number;
|
|
38
|
+
prOutcomeMinDecided: number;
|
|
39
|
+
warnings: string[];
|
|
40
|
+
};
|
|
41
|
+
export type PrOutcomeCalibrationInput = {
|
|
42
|
+
mergeConfirmed: number;
|
|
43
|
+
mergeFalse: number;
|
|
44
|
+
closeConfirmed: number;
|
|
45
|
+
closeFalse: number;
|
|
46
|
+
hold?: number | undefined;
|
|
47
|
+
observedAt?: string | undefined;
|
|
48
|
+
};
|
|
49
|
+
export type HistoricalReplayCalibrationInput = {
|
|
50
|
+
compositeScore: number | GateVerdictCompositeCalibrationScore;
|
|
51
|
+
replayRunId: string;
|
|
52
|
+
observedAt: string;
|
|
53
|
+
harnessStatus: ReplayHarnessStatus;
|
|
54
|
+
};
|
|
55
|
+
export type CalibrationSourceMetric = {
|
|
56
|
+
source: CalibrationSignalSource;
|
|
57
|
+
accuracy: number | null;
|
|
58
|
+
sampleSize: number;
|
|
59
|
+
observedAt: string | null;
|
|
60
|
+
fresh: boolean;
|
|
61
|
+
replayRunId?: string | undefined;
|
|
62
|
+
harnessStatus?: ReplayHarnessStatus | undefined;
|
|
63
|
+
};
|
|
64
|
+
export type Phase7CalibrationLoopResult = {
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
baselineAccuracy: number;
|
|
67
|
+
combinedAccuracy: number | null;
|
|
68
|
+
deltaFromBaseline: number | null;
|
|
69
|
+
weights: {
|
|
70
|
+
historicalReplay: number;
|
|
71
|
+
prOutcome: number;
|
|
72
|
+
};
|
|
73
|
+
bySource: {
|
|
74
|
+
historical_replay: CalibrationSourceMetric;
|
|
75
|
+
pr_outcome: CalibrationSourceMetric;
|
|
76
|
+
};
|
|
77
|
+
replayHarnessHold: boolean;
|
|
78
|
+
replayHarnessStatus: ReplayHarnessStatus | "missing";
|
|
79
|
+
autonomyIncreasePermitted: boolean;
|
|
80
|
+
holdReasons: string[];
|
|
81
|
+
replayRunDue: boolean;
|
|
82
|
+
audit: {
|
|
83
|
+
contributingSources: CalibrationSignalSource[];
|
|
84
|
+
rejectedSources: Array<{
|
|
85
|
+
source: CalibrationSignalSource;
|
|
86
|
+
reason: string;
|
|
87
|
+
}>;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Resolve the explicit Phase 7 loop config from a parsed `.gittensory-miner.yml`-style object. Default is disabled
|
|
92
|
+
* and fail-closed when enabled but inputs are missing or degraded.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolvePhase7CalibrationConfig(manifest: Phase7CalibrationManifest | Record<string, unknown> | null | undefined): Phase7CalibrationConfig;
|
|
95
|
+
/** Derive live pr_outcome calibration accuracy from a gate-eval-style confusion matrix. Pure. */
|
|
96
|
+
export declare function computePrOutcomeCalibrationAccuracy(input: PrOutcomeCalibrationInput): {
|
|
97
|
+
accuracy: number | null;
|
|
98
|
+
sampleSize: number;
|
|
99
|
+
};
|
|
100
|
+
/** True when a replay run is still fresh relative to the configured max age. Pure. */
|
|
101
|
+
export declare function isHistoricalReplayRunFresh(input: {
|
|
102
|
+
observedAt: string;
|
|
103
|
+
maxAgeHours: number;
|
|
104
|
+
now?: string | Date | null | undefined;
|
|
105
|
+
}): boolean;
|
|
106
|
+
/** Recommend whether a new historical-replay run should be scheduled/triggered. Pure. */
|
|
107
|
+
export declare function shouldScheduleHistoricalReplayRun(input: {
|
|
108
|
+
config: Phase7CalibrationConfig | Phase7CalibrationManifest | Record<string, unknown> | null | undefined;
|
|
109
|
+
lastReplayObservedAt?: string | null | undefined;
|
|
110
|
+
harnessStatus?: ReplayHarnessStatus | "missing" | undefined;
|
|
111
|
+
now?: string | Date | null | undefined;
|
|
112
|
+
}): {
|
|
113
|
+
due: boolean;
|
|
114
|
+
reason: string;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Combine historical-replay and pr_outcome calibration signals into the tracked Phase 7 metric, record provenance,
|
|
118
|
+
* and evaluate fail-closed autonomy-level increase eligibility.
|
|
119
|
+
*/
|
|
120
|
+
export declare function computePhase7CalibrationLoop(input: {
|
|
121
|
+
config?: Phase7CalibrationConfig | Phase7CalibrationManifest | Record<string, unknown> | null | undefined;
|
|
122
|
+
prOutcome?: PrOutcomeCalibrationInput | null | undefined;
|
|
123
|
+
historicalReplay?: HistoricalReplayCalibrationInput | null | undefined;
|
|
124
|
+
now?: string | Date | null | undefined;
|
|
125
|
+
}): Phase7CalibrationLoopResult;
|
|
126
|
+
/** Evaluate autonomy-level increase eligibility from a computed loop result. Pure alias for callers that split steps. */
|
|
127
|
+
export declare function evaluateAutonomyIncreaseEligibility(result: Phase7CalibrationLoopResult): {
|
|
128
|
+
permitted: boolean;
|
|
129
|
+
holdReasons: string[];
|
|
130
|
+
replayHarnessHold: boolean;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Render a deterministic, public-safe Markdown report for a Phase 7 calibration loop evaluation. Includes the
|
|
134
|
+
* tracked metric, baseline delta, per-source breakdown, replay cadence recommendation, and hold reasons.
|
|
135
|
+
*/
|
|
136
|
+
export declare function renderPhase7CalibrationAuditMarkdown(result: Phase7CalibrationLoopResult): string;
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
// Phase 7 historical-replay calibration loop (#3014).
|
|
2
|
+
//
|
|
3
|
+
// Pure engine contract for combining the historical-replay composite score with the passive pr_outcome signal,
|
|
4
|
+
// tracking calibration accuracy against the documented 62% baseline, and fail-closed gating of autonomy-level
|
|
5
|
+
// increases. The miner runtime owns scheduling replay runs and persisting ledger rows; this module owns the
|
|
6
|
+
// deterministic combine, freshness, threshold, and hold-reason logic.
|
|
7
|
+
/** Documented self-review calibration baseline from the Phase 7 roadmap (#2994). */
|
|
8
|
+
export const DOCUMENTED_CALIBRATION_BASELINE = 0.62;
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
phase7LoopEnabled: false,
|
|
11
|
+
autonomyIncreaseMinAccuracy: 0.7,
|
|
12
|
+
replayFreshnessMaxAgeHours: 168,
|
|
13
|
+
historicalReplayWeight: 0.5,
|
|
14
|
+
prOutcomeWeight: 0.5,
|
|
15
|
+
prOutcomeMinDecided: 10,
|
|
16
|
+
};
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
19
|
+
}
|
|
20
|
+
function roundScore(value) {
|
|
21
|
+
return Math.round(Math.min(1, Math.max(0, value)) * 1_000_000) / 1_000_000;
|
|
22
|
+
}
|
|
23
|
+
function finiteNonNegative(value, fallback) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return fallback;
|
|
26
|
+
if (!Number.isFinite(value) || value < 0)
|
|
27
|
+
return 0;
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function normalizeBoolean(value) {
|
|
31
|
+
if (typeof value === "boolean")
|
|
32
|
+
return value;
|
|
33
|
+
if (typeof value === "number") {
|
|
34
|
+
if (value === 1)
|
|
35
|
+
return true;
|
|
36
|
+
if (value === 0)
|
|
37
|
+
return false;
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value !== "string")
|
|
41
|
+
return undefined;
|
|
42
|
+
const normalized = value.trim().toLowerCase();
|
|
43
|
+
if (["true", "1", "yes", "on", "enabled"].includes(normalized))
|
|
44
|
+
return true;
|
|
45
|
+
if (["false", "0", "no", "off", "disabled"].includes(normalized))
|
|
46
|
+
return false;
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
function normalizeOptionalNumber(value) {
|
|
50
|
+
if (value === undefined || value === null)
|
|
51
|
+
return undefined;
|
|
52
|
+
const number = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : Number.NaN;
|
|
53
|
+
if (!Number.isFinite(number))
|
|
54
|
+
return undefined;
|
|
55
|
+
return number;
|
|
56
|
+
}
|
|
57
|
+
function normalizeOptionalPositiveInt(value, fallback) {
|
|
58
|
+
const number = normalizeOptionalNumber(value);
|
|
59
|
+
if (number === undefined || number <= 0)
|
|
60
|
+
return fallback;
|
|
61
|
+
return Math.max(1, Math.floor(number));
|
|
62
|
+
}
|
|
63
|
+
function normalizeObservedAt(value) {
|
|
64
|
+
if (!value)
|
|
65
|
+
return null;
|
|
66
|
+
const ms = Date.parse(value);
|
|
67
|
+
if (!Number.isFinite(ms))
|
|
68
|
+
return null;
|
|
69
|
+
return new Date(ms).toISOString();
|
|
70
|
+
}
|
|
71
|
+
function parseNow(value) {
|
|
72
|
+
if (value instanceof Date && Number.isFinite(value.getTime()))
|
|
73
|
+
return value;
|
|
74
|
+
const parsed = normalizeObservedAt(typeof value === "string" ? value : undefined);
|
|
75
|
+
return parsed ? new Date(parsed) : new Date();
|
|
76
|
+
}
|
|
77
|
+
function normalizeReplayRunId(value) {
|
|
78
|
+
const trimmed = value.trim();
|
|
79
|
+
if (!trimmed || trimmed.length > 160 || /[\r\n\0]/u.test(trimmed))
|
|
80
|
+
return null;
|
|
81
|
+
return trimmed;
|
|
82
|
+
}
|
|
83
|
+
function normalizeCompositeWeights(config) {
|
|
84
|
+
const raw = {
|
|
85
|
+
historicalReplay: finiteNonNegative(config.historicalReplayWeight, DEFAULT_CONFIG.historicalReplayWeight),
|
|
86
|
+
prOutcome: finiteNonNegative(config.prOutcomeWeight, DEFAULT_CONFIG.prOutcomeWeight),
|
|
87
|
+
};
|
|
88
|
+
const total = raw.historicalReplay + raw.prOutcome;
|
|
89
|
+
if (total <= 0) {
|
|
90
|
+
return { historicalReplay: DEFAULT_CONFIG.historicalReplayWeight, prOutcome: DEFAULT_CONFIG.prOutcomeWeight };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
historicalReplay: raw.historicalReplay / total,
|
|
94
|
+
prOutcome: raw.prOutcome / total,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function markdownSafe(value) {
|
|
98
|
+
return value.replace(/[\r\n]+/gu, " ").replace(/[\\`*_[\]<>|]/gu, "\\$&");
|
|
99
|
+
}
|
|
100
|
+
function markdownList(values) {
|
|
101
|
+
if (values.length === 0)
|
|
102
|
+
return "- none";
|
|
103
|
+
return values.map((value) => `- ${markdownSafe(value)}`).join("\n");
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve the explicit Phase 7 loop config from a parsed `.gittensory-miner.yml`-style object. Default is disabled
|
|
107
|
+
* and fail-closed when enabled but inputs are missing or degraded.
|
|
108
|
+
*/
|
|
109
|
+
export function resolvePhase7CalibrationConfig(manifest) {
|
|
110
|
+
const warnings = [];
|
|
111
|
+
const root = isRecord(manifest) ? manifest : {};
|
|
112
|
+
const miner = isRecord(root.miner) ? root.miner : {};
|
|
113
|
+
const minerCalibration = isRecord(miner.calibration) ? miner.calibration : {};
|
|
114
|
+
const topCalibration = isRecord(root.calibration) ? root.calibration : {};
|
|
115
|
+
const enabledRaw = minerCalibration.phase7LoopEnabled ?? topCalibration.phase7LoopEnabled ?? undefined;
|
|
116
|
+
const enabled = normalizeBoolean(enabledRaw);
|
|
117
|
+
if (enabledRaw !== undefined && enabled === undefined) {
|
|
118
|
+
warnings.push("miner.calibration.phase7LoopEnabled must be a boolean-like value; defaulting to false.");
|
|
119
|
+
}
|
|
120
|
+
const minAccuracyRaw = minerCalibration.autonomyIncreaseMinAccuracy ?? topCalibration.autonomyIncreaseMinAccuracy ?? undefined;
|
|
121
|
+
const minAccuracy = normalizeOptionalNumber(minAccuracyRaw);
|
|
122
|
+
if (minAccuracyRaw !== undefined && (minAccuracy === undefined || minAccuracy < 0 || minAccuracy > 1)) {
|
|
123
|
+
warnings.push("miner.calibration.autonomyIncreaseMinAccuracy must be a finite number in [0, 1]; using default 0.70.");
|
|
124
|
+
}
|
|
125
|
+
const freshnessRaw = minerCalibration.replayFreshnessMaxAgeHours ?? topCalibration.replayFreshnessMaxAgeHours ?? undefined;
|
|
126
|
+
const freshness = normalizeOptionalNumber(freshnessRaw);
|
|
127
|
+
if (freshnessRaw !== undefined && (freshness === undefined || freshness <= 0)) {
|
|
128
|
+
warnings.push("miner.calibration.replayFreshnessMaxAgeHours must be a positive finite number; using default 168.");
|
|
129
|
+
}
|
|
130
|
+
const replayWeightRaw = minerCalibration.historicalReplayWeight ?? topCalibration.historicalReplayWeight ?? undefined;
|
|
131
|
+
const replayWeight = normalizeOptionalNumber(replayWeightRaw);
|
|
132
|
+
if (replayWeightRaw !== undefined && (replayWeight === undefined || replayWeight < 0)) {
|
|
133
|
+
warnings.push("miner.calibration.historicalReplayWeight must be a non-negative finite number; using default 0.5.");
|
|
134
|
+
}
|
|
135
|
+
const prOutcomeWeightRaw = minerCalibration.prOutcomeWeight ?? topCalibration.prOutcomeWeight ?? undefined;
|
|
136
|
+
const prOutcomeWeight = normalizeOptionalNumber(prOutcomeWeightRaw);
|
|
137
|
+
if (prOutcomeWeightRaw !== undefined && (prOutcomeWeight === undefined || prOutcomeWeight < 0)) {
|
|
138
|
+
warnings.push("miner.calibration.prOutcomeWeight must be a non-negative finite number; using default 0.5.");
|
|
139
|
+
}
|
|
140
|
+
const minDecidedRaw = minerCalibration.prOutcomeMinDecided ?? topCalibration.prOutcomeMinDecided ?? undefined;
|
|
141
|
+
const minDecided = normalizeOptionalPositiveInt(minDecidedRaw, DEFAULT_CONFIG.prOutcomeMinDecided);
|
|
142
|
+
if (minDecidedRaw !== undefined && normalizeOptionalNumber(minDecidedRaw) === undefined) {
|
|
143
|
+
warnings.push("miner.calibration.prOutcomeMinDecided must be a positive integer; using default 10.");
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
phase7LoopEnabled: enabled === true,
|
|
147
|
+
autonomyIncreaseMinAccuracy: minAccuracy !== undefined && minAccuracy >= 0 && minAccuracy <= 1
|
|
148
|
+
? roundScore(minAccuracy)
|
|
149
|
+
: DEFAULT_CONFIG.autonomyIncreaseMinAccuracy,
|
|
150
|
+
replayFreshnessMaxAgeHours: freshness !== undefined && freshness > 0 ? freshness : DEFAULT_CONFIG.replayFreshnessMaxAgeHours,
|
|
151
|
+
historicalReplayWeight: replayWeight !== undefined && replayWeight >= 0 ? replayWeight : DEFAULT_CONFIG.historicalReplayWeight,
|
|
152
|
+
prOutcomeWeight: prOutcomeWeight !== undefined && prOutcomeWeight >= 0 ? prOutcomeWeight : DEFAULT_CONFIG.prOutcomeWeight,
|
|
153
|
+
prOutcomeMinDecided: minDecided,
|
|
154
|
+
warnings,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** Derive live pr_outcome calibration accuracy from a gate-eval-style confusion matrix. Pure. */
|
|
158
|
+
export function computePrOutcomeCalibrationAccuracy(input) {
|
|
159
|
+
const mergeConfirmed = finiteNonNegative(input.mergeConfirmed, 0);
|
|
160
|
+
const mergeFalse = finiteNonNegative(input.mergeFalse, 0);
|
|
161
|
+
const closeConfirmed = finiteNonNegative(input.closeConfirmed, 0);
|
|
162
|
+
const closeFalse = finiteNonNegative(input.closeFalse, 0);
|
|
163
|
+
const sampleSize = mergeConfirmed + mergeFalse + closeConfirmed + closeFalse;
|
|
164
|
+
if (sampleSize <= 0)
|
|
165
|
+
return { accuracy: null, sampleSize: 0 };
|
|
166
|
+
return {
|
|
167
|
+
accuracy: roundScore((mergeConfirmed + closeConfirmed) / sampleSize),
|
|
168
|
+
sampleSize,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** True when a replay run is still fresh relative to the configured max age. Pure. */
|
|
172
|
+
export function isHistoricalReplayRunFresh(input) {
|
|
173
|
+
const observed = normalizeObservedAt(input.observedAt);
|
|
174
|
+
if (!observed)
|
|
175
|
+
return false;
|
|
176
|
+
const maxAgeHours = finiteNonNegative(input.maxAgeHours, DEFAULT_CONFIG.replayFreshnessMaxAgeHours);
|
|
177
|
+
if (maxAgeHours <= 0)
|
|
178
|
+
return false;
|
|
179
|
+
const ageMs = parseNow(input.now).getTime() - new Date(observed).getTime();
|
|
180
|
+
if (!Number.isFinite(ageMs) || ageMs < 0)
|
|
181
|
+
return false;
|
|
182
|
+
return ageMs <= maxAgeHours * 3_600_000;
|
|
183
|
+
}
|
|
184
|
+
/** Recommend whether a new historical-replay run should be scheduled/triggered. Pure. */
|
|
185
|
+
export function shouldScheduleHistoricalReplayRun(input) {
|
|
186
|
+
const config = input.config && "phase7LoopEnabled" in input.config
|
|
187
|
+
? input.config
|
|
188
|
+
: resolvePhase7CalibrationConfig(input.config);
|
|
189
|
+
if (!config.phase7LoopEnabled) {
|
|
190
|
+
return { due: false, reason: "phase7_loop_disabled" };
|
|
191
|
+
}
|
|
192
|
+
if (input.harnessStatus === "unavailable" || input.harnessStatus === "degraded") {
|
|
193
|
+
return { due: true, reason: `replay_harness_${input.harnessStatus}` };
|
|
194
|
+
}
|
|
195
|
+
if (!input.lastReplayObservedAt) {
|
|
196
|
+
return { due: true, reason: "no_replay_run_recorded" };
|
|
197
|
+
}
|
|
198
|
+
if (!isHistoricalReplayRunFresh({
|
|
199
|
+
observedAt: input.lastReplayObservedAt,
|
|
200
|
+
maxAgeHours: config.replayFreshnessMaxAgeHours,
|
|
201
|
+
now: input.now,
|
|
202
|
+
})) {
|
|
203
|
+
return { due: true, reason: "replay_run_stale" };
|
|
204
|
+
}
|
|
205
|
+
return { due: false, reason: "replay_run_fresh" };
|
|
206
|
+
}
|
|
207
|
+
function extractHistoricalReplayScore(compositeScore) {
|
|
208
|
+
const rawScore = typeof compositeScore === "number" ? compositeScore : compositeScore.compositeScore;
|
|
209
|
+
if (!Number.isFinite(rawScore))
|
|
210
|
+
return null;
|
|
211
|
+
return roundScore(rawScore);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Combine historical-replay and pr_outcome calibration signals into the tracked Phase 7 metric, record provenance,
|
|
215
|
+
* and evaluate fail-closed autonomy-level increase eligibility.
|
|
216
|
+
*/
|
|
217
|
+
export function computePhase7CalibrationLoop(input) {
|
|
218
|
+
const config = input.config && "phase7LoopEnabled" in input.config
|
|
219
|
+
? input.config
|
|
220
|
+
: resolvePhase7CalibrationConfig(input.config);
|
|
221
|
+
const weights = normalizeCompositeWeights(config);
|
|
222
|
+
const now = parseNow(input.now);
|
|
223
|
+
const holdReasons = [];
|
|
224
|
+
const contributingSources = [];
|
|
225
|
+
const rejectedSources = [];
|
|
226
|
+
const prOutcomeDerived = input.prOutcome ? computePrOutcomeCalibrationAccuracy(input.prOutcome) : null;
|
|
227
|
+
const prOutcomeMetric = {
|
|
228
|
+
source: "pr_outcome",
|
|
229
|
+
accuracy: prOutcomeDerived?.accuracy ?? null,
|
|
230
|
+
sampleSize: prOutcomeDerived?.sampleSize ?? 0,
|
|
231
|
+
observedAt: normalizeObservedAt(input.prOutcome?.observedAt),
|
|
232
|
+
fresh: true,
|
|
233
|
+
};
|
|
234
|
+
if (prOutcomeDerived && prOutcomeDerived.sampleSize >= config.prOutcomeMinDecided && prOutcomeDerived.accuracy !== null) {
|
|
235
|
+
contributingSources.push("pr_outcome");
|
|
236
|
+
}
|
|
237
|
+
else if (input.prOutcome) {
|
|
238
|
+
rejectedSources.push({
|
|
239
|
+
source: "pr_outcome",
|
|
240
|
+
reason: prOutcomeDerived && prOutcomeDerived.sampleSize > 0
|
|
241
|
+
? "insufficient_pr_outcome_samples"
|
|
242
|
+
: "no_pr_outcome_signal",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
let replayHarnessStatus = "missing";
|
|
246
|
+
let replayHarnessHold = false;
|
|
247
|
+
let historicalReplayMetric = {
|
|
248
|
+
source: "historical_replay",
|
|
249
|
+
accuracy: null,
|
|
250
|
+
sampleSize: 0,
|
|
251
|
+
observedAt: null,
|
|
252
|
+
fresh: false,
|
|
253
|
+
};
|
|
254
|
+
if (input.historicalReplay) {
|
|
255
|
+
const replayRunId = normalizeReplayRunId(input.historicalReplay.replayRunId);
|
|
256
|
+
const observedAt = normalizeObservedAt(input.historicalReplay.observedAt);
|
|
257
|
+
replayHarnessStatus = input.historicalReplay.harnessStatus;
|
|
258
|
+
const fresh = observedAt !== null &&
|
|
259
|
+
isHistoricalReplayRunFresh({
|
|
260
|
+
observedAt,
|
|
261
|
+
maxAgeHours: config.replayFreshnessMaxAgeHours,
|
|
262
|
+
now,
|
|
263
|
+
});
|
|
264
|
+
const accuracy = extractHistoricalReplayScore(input.historicalReplay.compositeScore);
|
|
265
|
+
historicalReplayMetric = {
|
|
266
|
+
source: "historical_replay",
|
|
267
|
+
accuracy,
|
|
268
|
+
sampleSize: 1,
|
|
269
|
+
observedAt,
|
|
270
|
+
fresh,
|
|
271
|
+
replayRunId: replayRunId ?? undefined,
|
|
272
|
+
harnessStatus: input.historicalReplay.harnessStatus,
|
|
273
|
+
};
|
|
274
|
+
if (input.historicalReplay.harnessStatus !== "healthy") {
|
|
275
|
+
replayHarnessHold = true;
|
|
276
|
+
holdReasons.push(`replay_harness_${input.historicalReplay.harnessStatus}`);
|
|
277
|
+
rejectedSources.push({
|
|
278
|
+
source: "historical_replay",
|
|
279
|
+
reason: `replay_harness_${input.historicalReplay.harnessStatus}`,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
else if (!replayRunId || !observedAt) {
|
|
283
|
+
replayHarnessHold = true;
|
|
284
|
+
holdReasons.push("invalid_replay_run_metadata");
|
|
285
|
+
rejectedSources.push({ source: "historical_replay", reason: "invalid_replay_run_metadata" });
|
|
286
|
+
}
|
|
287
|
+
else if (!fresh) {
|
|
288
|
+
replayHarnessHold = true;
|
|
289
|
+
holdReasons.push("replay_run_stale");
|
|
290
|
+
rejectedSources.push({ source: "historical_replay", reason: "replay_run_stale" });
|
|
291
|
+
}
|
|
292
|
+
else if (accuracy === null) {
|
|
293
|
+
replayHarnessHold = true;
|
|
294
|
+
holdReasons.push("invalid_replay_score");
|
|
295
|
+
rejectedSources.push({ source: "historical_replay", reason: "invalid_replay_score" });
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
contributingSources.push("historical_replay");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else if (config.phase7LoopEnabled) {
|
|
302
|
+
replayHarnessHold = true;
|
|
303
|
+
holdReasons.push("no_historical_replay_signal");
|
|
304
|
+
rejectedSources.push({ source: "historical_replay", reason: "no_historical_replay_signal" });
|
|
305
|
+
}
|
|
306
|
+
const usable = {
|
|
307
|
+
historical_replay: historicalReplayMetric.accuracy !== null &&
|
|
308
|
+
contributingSources.includes("historical_replay")
|
|
309
|
+
? { accuracy: historicalReplayMetric.accuracy, weight: weights.historicalReplay }
|
|
310
|
+
: null,
|
|
311
|
+
pr_outcome: prOutcomeMetric.accuracy !== null && contributingSources.includes("pr_outcome")
|
|
312
|
+
? { accuracy: prOutcomeMetric.accuracy, weight: weights.prOutcome }
|
|
313
|
+
: null,
|
|
314
|
+
};
|
|
315
|
+
const weightTotal = (usable.historical_replay?.weight ?? 0) + (usable.pr_outcome?.weight ?? 0);
|
|
316
|
+
const combinedAccuracy = weightTotal <= 0
|
|
317
|
+
? null
|
|
318
|
+
: roundScore(((usable.historical_replay?.accuracy ?? 0) * (usable.historical_replay?.weight ?? 0) +
|
|
319
|
+
(usable.pr_outcome?.accuracy ?? 0) * (usable.pr_outcome?.weight ?? 0)) /
|
|
320
|
+
weightTotal);
|
|
321
|
+
const deltaFromBaseline = combinedAccuracy === null ? null : roundScore(combinedAccuracy - DOCUMENTED_CALIBRATION_BASELINE);
|
|
322
|
+
const schedule = shouldScheduleHistoricalReplayRun({
|
|
323
|
+
config,
|
|
324
|
+
lastReplayObservedAt: historicalReplayMetric.observedAt,
|
|
325
|
+
harnessStatus: replayHarnessStatus,
|
|
326
|
+
now,
|
|
327
|
+
});
|
|
328
|
+
let autonomyIncreasePermitted = true;
|
|
329
|
+
if (config.phase7LoopEnabled) {
|
|
330
|
+
autonomyIncreasePermitted = false;
|
|
331
|
+
if (replayHarnessHold) {
|
|
332
|
+
// fail-closed: degraded/unavailable/stale/missing replay blocks increases without silent pr_outcome fallback
|
|
333
|
+
}
|
|
334
|
+
else if (combinedAccuracy === null) {
|
|
335
|
+
holdReasons.push("no_combined_calibration_signal");
|
|
336
|
+
}
|
|
337
|
+
else if (combinedAccuracy < config.autonomyIncreaseMinAccuracy) {
|
|
338
|
+
holdReasons.push("calibration_below_threshold");
|
|
339
|
+
}
|
|
340
|
+
else if (!contributingSources.includes("historical_replay") || !contributingSources.includes("pr_outcome")) {
|
|
341
|
+
holdReasons.push("missing_required_signal_source");
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
autonomyIncreasePermitted = true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (!autonomyIncreasePermitted && holdReasons.length === 0) {
|
|
348
|
+
holdReasons.push("phase7_loop_hold");
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
enabled: config.phase7LoopEnabled,
|
|
352
|
+
baselineAccuracy: DOCUMENTED_CALIBRATION_BASELINE,
|
|
353
|
+
combinedAccuracy,
|
|
354
|
+
deltaFromBaseline,
|
|
355
|
+
weights,
|
|
356
|
+
bySource: {
|
|
357
|
+
historical_replay: historicalReplayMetric,
|
|
358
|
+
pr_outcome: prOutcomeMetric,
|
|
359
|
+
},
|
|
360
|
+
replayHarnessHold,
|
|
361
|
+
replayHarnessStatus,
|
|
362
|
+
autonomyIncreasePermitted,
|
|
363
|
+
holdReasons: [...new Set(holdReasons)],
|
|
364
|
+
replayRunDue: schedule.due,
|
|
365
|
+
audit: {
|
|
366
|
+
contributingSources,
|
|
367
|
+
rejectedSources,
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/** Evaluate autonomy-level increase eligibility from a computed loop result. Pure alias for callers that split steps. */
|
|
372
|
+
export function evaluateAutonomyIncreaseEligibility(result) {
|
|
373
|
+
return {
|
|
374
|
+
permitted: result.autonomyIncreasePermitted,
|
|
375
|
+
holdReasons: result.holdReasons,
|
|
376
|
+
replayHarnessHold: result.replayHarnessHold,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Render a deterministic, public-safe Markdown report for a Phase 7 calibration loop evaluation. Includes the
|
|
381
|
+
* tracked metric, baseline delta, per-source breakdown, replay cadence recommendation, and hold reasons.
|
|
382
|
+
*/
|
|
383
|
+
export function renderPhase7CalibrationAuditMarkdown(result) {
|
|
384
|
+
const formatAccuracy = (value) => (value === null ? "n/a" : `${(value * 100).toFixed(2)}%`);
|
|
385
|
+
const lines = [
|
|
386
|
+
"# Phase 7 Calibration Loop",
|
|
387
|
+
"",
|
|
388
|
+
`- loop enabled: ${result.enabled}`,
|
|
389
|
+
`- documented baseline: ${(result.baselineAccuracy * 100).toFixed(2)}%`,
|
|
390
|
+
`- combined calibration accuracy: ${formatAccuracy(result.combinedAccuracy)}`,
|
|
391
|
+
`- delta from baseline: ${result.deltaFromBaseline === null ? "n/a" : `${(result.deltaFromBaseline * 100).toFixed(2)} percentage points`}`,
|
|
392
|
+
`- autonomy increase permitted: ${result.autonomyIncreasePermitted}`,
|
|
393
|
+
`- replay harness hold: ${result.replayHarnessHold}`,
|
|
394
|
+
`- replay harness status: ${result.replayHarnessStatus}`,
|
|
395
|
+
`- replay run due: ${result.replayRunDue}`,
|
|
396
|
+
"",
|
|
397
|
+
"## Effective Weights",
|
|
398
|
+
"",
|
|
399
|
+
`- historical_replay: ${result.weights.historicalReplay.toFixed(6)}`,
|
|
400
|
+
`- pr_outcome: ${result.weights.prOutcome.toFixed(6)}`,
|
|
401
|
+
"",
|
|
402
|
+
"## Signal Sources",
|
|
403
|
+
"",
|
|
404
|
+
"### historical_replay",
|
|
405
|
+
"",
|
|
406
|
+
`- accuracy: ${formatAccuracy(result.bySource.historical_replay.accuracy)}`,
|
|
407
|
+
`- sampleSize: ${result.bySource.historical_replay.sampleSize}`,
|
|
408
|
+
`- observedAt: ${result.bySource.historical_replay.observedAt ?? "n/a"}`,
|
|
409
|
+
`- fresh: ${result.bySource.historical_replay.fresh}`,
|
|
410
|
+
`- replayRunId: ${result.bySource.historical_replay.replayRunId ? markdownSafe(result.bySource.historical_replay.replayRunId) : "n/a"}`,
|
|
411
|
+
`- harnessStatus: ${result.bySource.historical_replay.harnessStatus ?? "n/a"}`,
|
|
412
|
+
"",
|
|
413
|
+
"### pr_outcome",
|
|
414
|
+
"",
|
|
415
|
+
`- accuracy: ${formatAccuracy(result.bySource.pr_outcome.accuracy)}`,
|
|
416
|
+
`- sampleSize: ${result.bySource.pr_outcome.sampleSize}`,
|
|
417
|
+
`- observedAt: ${result.bySource.pr_outcome.observedAt ?? "n/a"}`,
|
|
418
|
+
"",
|
|
419
|
+
"## Hold Reasons",
|
|
420
|
+
"",
|
|
421
|
+
markdownList(result.holdReasons),
|
|
422
|
+
"",
|
|
423
|
+
"## Contributing Sources",
|
|
424
|
+
"",
|
|
425
|
+
markdownList(result.audit.contributingSources),
|
|
426
|
+
"",
|
|
427
|
+
"## Rejected Sources",
|
|
428
|
+
"",
|
|
429
|
+
];
|
|
430
|
+
if (result.audit.rejectedSources.length === 0) {
|
|
431
|
+
lines.push("- none");
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
lines.push("| Source | Reason |", "| --- | --- |", ...result.audit.rejectedSources.map((row) => `| ${markdownSafe(row.source)} | ${markdownSafe(row.reason)} |`));
|
|
435
|
+
}
|
|
436
|
+
return `${lines.join("\n")}\n`;
|
|
437
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PlanDag } from "./plan-export.js";
|
|
2
|
+
/**
|
|
3
|
+
* Return whether the plan is deadlocked: pending steps remain but none are runnable. Mirrors the `blocked`
|
|
4
|
+
* branch of hosted `planProgress` — failed or running plans are not considered blocked. Pure.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isPlanBlocked(plan: PlanDag): boolean;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const isDone = (status) => status === "completed" || status === "skipped";
|
|
2
|
+
function nextReadySteps(plan) {
|
|
3
|
+
const statusById = new Map(plan.steps.map((step) => [step.id, step.status]));
|
|
4
|
+
return plan.steps.filter((step) => step.status === "pending" && step.dependsOn.every((dep) => isDone(statusById.get(dep) ?? "pending")));
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Return whether the plan is deadlocked: pending steps remain but none are runnable. Mirrors the `blocked`
|
|
8
|
+
* branch of hosted `planProgress` — failed or running plans are not considered blocked. Pure.
|
|
9
|
+
*/
|
|
10
|
+
export function isPlanBlocked(plan) {
|
|
11
|
+
const total = plan.steps.length;
|
|
12
|
+
if (total === 0)
|
|
13
|
+
return false;
|
|
14
|
+
const completed = plan.steps.filter((step) => step.status === "completed").length;
|
|
15
|
+
const skipped = plan.steps.filter((step) => step.status === "skipped").length;
|
|
16
|
+
if (completed + skipped === total)
|
|
17
|
+
return false;
|
|
18
|
+
if (plan.steps.some((step) => step.status === "failed"))
|
|
19
|
+
return false;
|
|
20
|
+
if (plan.steps.some((step) => step.status === "running"))
|
|
21
|
+
return false;
|
|
22
|
+
const pending = plan.steps.some((step) => step.status === "pending");
|
|
23
|
+
return pending && nextReadySteps(plan).length === 0;
|
|
24
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return whether every step in the plan is completed. Empty plans are not considered complete.
|
|
3
|
+
* Pure — reads the plan DAG only.
|
|
4
|
+
*/
|
|
5
|
+
export function isPlanFullyCompleted(plan) {
|
|
6
|
+
return plan.steps.length > 0 && plan.steps.every((step) => step.status === "completed");
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type PlanStepStatus = "pending" | "running" | "completed" | "failed" | "skipped";
|
|
2
|
+
export type PlanStep = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
actionClass?: string | undefined;
|
|
6
|
+
dependsOn: string[];
|
|
7
|
+
status: PlanStepStatus;
|
|
8
|
+
attempts: number;
|
|
9
|
+
maxAttempts: number;
|
|
10
|
+
lastError?: string | null | undefined;
|
|
11
|
+
};
|
|
12
|
+
export type PlanDag = {
|
|
13
|
+
steps: PlanStep[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Render a plan DAG as a Markdown checklist ordered by dependency: one `- [x]`/`- [ ]` line per step (checked when
|
|
17
|
+
* the step is completed), annotated with its status, its attempt count when it has run, and its last error when
|
|
18
|
+
* present. Display fields are collapsed to a single line and Markdown control characters are escaped so each step
|
|
19
|
+
* stays on one row and an untrusted title/error cannot re-style it. Pure — it reads the plan and returns a string.
|
|
20
|
+
*/
|
|
21
|
+
export declare function renderPlanAsMarkdown(plan: PlanDag): string;
|
|
22
|
+
/**
|
|
23
|
+
* Render a plan DAG as a stable, deterministically key-ordered JSON string. Two renders of the identical plan are
|
|
24
|
+
* byte-identical (object keys are sorted at every level; array order is preserved), which makes plan snapshots
|
|
25
|
+
* diffable across runs. Pure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function renderPlanAsJson(plan: PlanDag): string;
|