Skip to content

Commit 7e2e20d

Browse files
feat(miner-selfimprove): wire the historical-replay scorer into the Phase 7 calibration loop (#4248) (#5462)
Co-authored-by: Andriy Polanski <andriy.polanski@gmail.com>
1 parent 7e89fae commit 7e2e20d

7 files changed

Lines changed: 963 additions & 1 deletion

File tree

packages/gittensory-miner/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ The package also includes an append-only prediction ledger: `initPredictionLedge
5050
codes, plus the producing `ENGINE_VERSION`) in local SQLite, so a later self-improve pass can score predictions
5151
against realized outcomes. Insert-only. (#4263)
5252

53+
The package also includes the Phase 7 calibration runner: `runHistoricalReplayCalibrationCycle`
54+
(`lib/calibration-run.js`) scores a completed historical-replay run with the deterministic objective-anchor scorer,
55+
folds the composite into the engine's `computePhase7CalibrationLoop` combine alongside the existing `pr_outcome`
56+
signal, and persists the combined snapshot as a `calibration_snapshot` event — queryable with
57+
`gittensory-miner ledger list --type calibration_snapshot` or `readCalibrationSnapshots` /
58+
`latestCalibrationSnapshot`. It measures and records only; acting on the metric (autonomy bumps, threshold tuning)
59+
stays maintainer-only. See [`docs/miner-selfimprove-calibration.md`](docs/miner-selfimprove-calibration.md). (#4248)
60+
5361
`gittensory-miner manage status` now also folds each tracked repo's current discover/plan/prepare run state
5462
(`run-state.js`) alongside its managed PR rows into a "run portfolio" view — `collectRunPortfolio` /
5563
`renderRunPortfolioTable` — so a repo actively being discovered or planned shows up even with zero PRs yet.

packages/gittensory-miner/docs/miner-selfimprove-calibration.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@ project, how many predictions were `wouldMerge` vs `wouldClose`, and how each re
2626
error)**, and symmetrically for close. `mergePrecision` / `closePrecision` are the headline accuracy numbers a
2727
dashboard renders.
2828

29+
## The runner: wiring the replay scorer into the combine (#4248)
30+
31+
`computePhase7CalibrationLoop` is a **pure combine contract** — by design it cannot schedule replay runs or read
32+
ledgers (the miner depends on the engine, not the reverse), so it waits for an external caller to feed it a real
33+
`historical_replay` composite. #3014 (PR #3225) shipped only that engine side; a 2026-07-08 audit found the closed
34+
issue claimed the two halves were "wired" when in fact **no miner-side runner ever called the replay scorer
35+
(`computeObjectiveAnchor`, #3012) and passed its result into the combine** — two finished pieces, never connected.
36+
37+
[`lib/calibration-run.js`](../lib/calibration-run.js) (#4248) is that missing runner.
38+
`runHistoricalReplayCalibrationCycle` scores a completed replay run with the deterministic objective-anchor scorer,
39+
reduces the per-task scores to one composite `[0, 1]`, folds it into the `HistoricalReplayCalibrationInput` shape the
40+
engine expects, calls `computePhase7CalibrationLoop` with that **plus the existing `pr_outcome` signal**, and
41+
persists the combined snapshot as a `calibration_snapshot` event on the local append-only event ledger (the same
42+
typed-event-over-`event-ledger.js` pattern as `pr-outcome.js`). The persisted metric is queryable with
43+
`gittensory-miner ledger list --type calibration_snapshot` and via `readCalibrationSnapshots` /
44+
`latestCalibrationSnapshot`.
45+
46+
Consistent with the boundary below, the runner is **read/measure-only**: it produces and persists the tracked
47+
metric but never acts on it (no autonomy bump, no threshold tune). Acting on the combined accuracy — the
48+
calibration-gated circuit-breaker — remains maintainer-only (#2352).
49+
2950
## Value-weighting: durable correctness, not volume
3051

3152
Raw merge/close precision is not the real objective, and a contributor reading a dashboard number should understand
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import type {
2+
HistoricalReplayCalibrationInput,
3+
Phase7CalibrationConfig,
4+
Phase7CalibrationLoopResult,
5+
Phase7CalibrationManifest,
6+
PrOutcomeCalibrationInput,
7+
ReplayHarnessStatus,
8+
} from "@jsonbored/gittensory-engine";
9+
10+
import type { AppendEventInput, LedgerEntry } from "./event-ledger.js";
11+
import type {
12+
ObjectiveAnchorResult,
13+
ReplayPlanInput,
14+
RevealedHistoryEntry,
15+
} from "./replay-objective-anchor.js";
16+
17+
export const MINER_CALIBRATION_SNAPSHOT_EVENT: "calibration_snapshot";
18+
19+
/** One completed replay-run task result: what the replay targeted, and the revealed post-T history to score it. */
20+
export interface ReplayTaskResult {
21+
replayPlan?: ReplayPlanInput | null;
22+
revealedHistory?: RevealedHistoryEntry[] | RevealedHistoryEntry | null;
23+
}
24+
25+
export interface ScoreCompositeOptions {
26+
computeObjectiveAnchor?: (
27+
input: { replayPlan?: ReplayPlanInput | null; revealedHistory?: RevealedHistoryEntry[] | RevealedHistoryEntry | null },
28+
) => ObjectiveAnchorResult;
29+
}
30+
31+
export interface HistoricalReplayCompositeScore {
32+
compositeScore: number | null;
33+
sampleSize: number;
34+
scores: number[];
35+
}
36+
37+
export function scoreHistoricalReplayComposite(
38+
replayResults: readonly ReplayTaskResult[] | null | undefined,
39+
options?: ScoreCompositeOptions,
40+
): HistoricalReplayCompositeScore;
41+
42+
/** A completed replay run's descriptor: its per-task results plus the run's identity/freshness/harness health. */
43+
export interface ReplayRunDescriptor {
44+
replayResults?: readonly ReplayTaskResult[] | null;
45+
replayRunId?: string;
46+
observedAt?: string;
47+
harnessStatus?: ReplayHarnessStatus;
48+
}
49+
50+
export interface BuiltHistoricalReplayInput {
51+
historicalReplay: HistoricalReplayCalibrationInput | null;
52+
compositeScore: number | null;
53+
sampleSize: number;
54+
scores: number[];
55+
}
56+
57+
export function buildHistoricalReplayCalibrationInput(
58+
replayRun: ReplayRunDescriptor | null | undefined,
59+
options?: ScoreCompositeOptions,
60+
): BuiltHistoricalReplayInput;
61+
62+
/** The persisted, public-safe projection of a Phase7CalibrationLoopResult. */
63+
export interface CalibrationSnapshotPayload {
64+
enabled: boolean;
65+
combinedAccuracy: number | null;
66+
baselineAccuracy: number;
67+
deltaFromBaseline: number | null;
68+
autonomyIncreasePermitted: boolean;
69+
replayHarnessHold: boolean;
70+
replayHarnessStatus: string;
71+
replayRunDue: boolean;
72+
holdReasons: string[];
73+
contributingSources: string[];
74+
replayRunId: string | null;
75+
observedAt: string | null;
76+
replaySampleSize: number;
77+
}
78+
79+
export interface SnapshotMeta {
80+
replayRunId?: string | null;
81+
observedAt?: string | null;
82+
sampleSize?: number;
83+
}
84+
85+
export function snapshotPayloadFromResult(
86+
result: Phase7CalibrationLoopResult,
87+
meta?: SnapshotMeta,
88+
): CalibrationSnapshotPayload;
89+
90+
export function normalizeCalibrationSnapshotPayload(payload: unknown): CalibrationSnapshotPayload | null;
91+
92+
export interface RecordCalibrationSnapshotOptions {
93+
/** Optional at the type level so a caller can pass an unusable ledger to exercise the fail-closed guard; the
94+
* writer throws `invalid_event_ledger` at runtime when this is absent or lacks `appendEvent`. */
95+
eventLedger?: { appendEvent(event: AppendEventInput): LedgerEntry };
96+
repoFullName?: string;
97+
}
98+
99+
export function recordCalibrationSnapshot(
100+
input: unknown,
101+
options?: RecordCalibrationSnapshotOptions,
102+
): LedgerEntry | null;
103+
104+
export interface CalibrationSnapshotReader {
105+
readEvents(filter?: { since?: number | null; repoFullName?: string | null }): unknown[];
106+
}
107+
108+
export interface CalibrationSnapshotFilter {
109+
since?: number | null;
110+
repoFullName?: string | null;
111+
}
112+
113+
export interface PersistedCalibrationSnapshot extends CalibrationSnapshotPayload {
114+
repoFullName: string | null;
115+
seq: number | null;
116+
createdAt: string | null;
117+
}
118+
119+
export function readCalibrationSnapshots(
120+
eventLedger: CalibrationSnapshotReader,
121+
filter?: CalibrationSnapshotFilter,
122+
): PersistedCalibrationSnapshot[];
123+
124+
export function latestCalibrationSnapshot(
125+
eventLedger: CalibrationSnapshotReader,
126+
filter?: CalibrationSnapshotFilter,
127+
): PersistedCalibrationSnapshot | null;
128+
129+
export interface RunCalibrationCycleInput {
130+
config?: Phase7CalibrationConfig | Phase7CalibrationManifest | Record<string, unknown> | null;
131+
prOutcome?: PrOutcomeCalibrationInput | null;
132+
replayRun?: ReplayRunDescriptor | null;
133+
now?: string | Date | null;
134+
observedAt?: string | null;
135+
repoFullName?: string;
136+
}
137+
138+
export interface RunCalibrationCycleDeps extends ScoreCompositeOptions {
139+
computeLoop?: (input: {
140+
config?: Phase7CalibrationConfig | Phase7CalibrationManifest | Record<string, unknown> | null;
141+
prOutcome?: PrOutcomeCalibrationInput | null;
142+
historicalReplay?: HistoricalReplayCalibrationInput | null;
143+
now?: string | Date | null;
144+
}) => Phase7CalibrationLoopResult;
145+
eventLedger?: { appendEvent(event: AppendEventInput): LedgerEntry };
146+
}
147+
148+
export interface RunCalibrationCycleResult {
149+
result: Phase7CalibrationLoopResult;
150+
snapshot: CalibrationSnapshotPayload;
151+
recorded: LedgerEntry | null;
152+
historicalReplay: HistoricalReplayCalibrationInput | null;
153+
compositeScore: number | null;
154+
sampleSize: number;
155+
scores: number[];
156+
}
157+
158+
export function runHistoricalReplayCalibrationCycle(
159+
input?: RunCalibrationCycleInput,
160+
deps?: RunCalibrationCycleDeps,
161+
): RunCalibrationCycleResult;

0 commit comments

Comments
 (0)