@dev-loops/core 0.1.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/bin/capture-deep-persona-signals.mjs +143 -0
- package/bin/ensure-phase-files.mjs +7 -0
- package/bin/log-bash-exit-1.mjs +7 -0
- package/bin/parse-review-threads.mjs +7 -0
- package/package.json +78 -0
- package/src/analysis/change-classifier.mjs +146 -0
- package/src/analysis/diff-analyzer.mjs +285 -0
- package/src/bash-exit-one.mjs +130 -0
- package/src/cli/helpers.mjs +22 -0
- package/src/cli/primitives.mjs +70 -0
- package/src/cli/retry-wrapper.mjs +169 -0
- package/src/cli/subcommand-runner.mjs +246 -0
- package/src/config/config.mjs +965 -0
- package/src/debt/cluster.mjs +240 -0
- package/src/debt/debt-finding.mjs +68 -0
- package/src/debt/debt-signal.mjs +46 -0
- package/src/debt/deep-persona-signals.mjs +266 -0
- package/src/debt/remediation-to-issue.mjs +121 -0
- package/src/debt/score.mjs +127 -0
- package/src/debt/shape.mjs +214 -0
- package/src/github/copilot-helpers.mjs +343 -0
- package/src/github/repo-slug.mjs +105 -0
- package/src/github/review-threads.mjs +343 -0
- package/src/harness/adapter.mjs +57 -0
- package/src/harness/index.mjs +3 -0
- package/src/harness/noop-adapter.mjs +22 -0
- package/src/harness/pi-adapter.mjs +47 -0
- package/src/loop/async-start-contract.mjs +170 -0
- package/src/loop/conductor-routing.mjs +817 -0
- package/src/loop/copilot-ci-status.mjs +255 -0
- package/src/loop/copilot-loop-iterations.mjs +161 -0
- package/src/loop/copilot-loop-state.mjs +510 -0
- package/src/loop/handoff-envelope.mjs +800 -0
- package/src/loop/issue-refinement-artifact.mjs +268 -0
- package/src/loop/lifecycle-state.mjs +342 -0
- package/src/loop/phase-files.mjs +187 -0
- package/src/loop/policy-constants.mjs +17 -0
- package/src/loop/pr-gate-coordination.mjs +1278 -0
- package/src/loop/public-dev-loop-routing-contract.mjs +277 -0
- package/src/loop/public-dev-loop-routing.mjs +1746 -0
- package/src/loop/queue-board-ordering.mjs +38 -0
- package/src/loop/queue-board-sync.mjs +223 -0
- package/src/loop/queue-driver.mjs +164 -0
- package/src/loop/queue-parallel.mjs +190 -0
- package/src/loop/queue-state.mjs +230 -0
- package/src/loop/retrospective-checkpoint.mjs +178 -0
- package/src/loop/reviewer-loop-state.mjs +456 -0
- package/src/loop/run-inspection.mjs +604 -0
- package/src/loop/steering.mjs +793 -0
- package/src/loop/timeout-policy.mjs +73 -0
- package/src/loop/tracker-first-loop-state.mjs +87 -0
- package/src/loop/tracker-pr-state.mjs +301 -0
- package/src/loop/worktree-guard.mjs +141 -0
- package/src/refinement/ac-dod-matrix.mjs +95 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure snapshot composer for the Copilot PR outer-loop run inspection surface.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a read-only, JSON-first inspection snapshot for one
|
|
5
|
+
* explicitly targeted run in the Copilot PR outer-loop family.
|
|
6
|
+
*
|
|
7
|
+
* It composes already-fetched inner-loop facts into the canonical inspection
|
|
8
|
+
* shape without performing any I/O, checkpoint writes, or state mutations.
|
|
9
|
+
*
|
|
10
|
+
* Schema version: 1
|
|
11
|
+
*
|
|
12
|
+
* Always-present output fields:
|
|
13
|
+
* ok, schemaVersion, target, inspectedAt, activeStateFamily,
|
|
14
|
+
* outerAction, activeFamilyState, statusClass, needsAttention,
|
|
15
|
+
* sourceMode, trust, evidence, markers
|
|
16
|
+
*
|
|
17
|
+
* Best-effort output fields:
|
|
18
|
+
* loopIterations, layers (copilot, reviewer, steering drill-down)
|
|
19
|
+
*
|
|
20
|
+
* Source precedence:
|
|
21
|
+
* 1. Authoritative live detector-backed facts
|
|
22
|
+
* 2. Bounded local checkpoint artifacts
|
|
23
|
+
* 3. Unknown/unavailable markers when neither is sufficient
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { summarizeLoopInterpretation } from "./copilot-loop-state.mjs";
|
|
27
|
+
import { isKnownOuterState } from "./conductor-routing.mjs";
|
|
28
|
+
import { getAllowedTransitions, lifecyclePhaseForCopilotState, resolveLifecycleState } from "./lifecycle-state.mjs";
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Constants
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
export const SCHEMA_VERSION = 1;
|
|
35
|
+
|
|
36
|
+
/** The workflow family this module inspects. */
|
|
37
|
+
export const ACTIVE_STATE_FAMILY = "copilot-pr-outer-loop";
|
|
38
|
+
|
|
39
|
+
export function deriveRunIdForInspectionTarget({ pr }) {
|
|
40
|
+
return `pr-${pr}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Top-level status class values. */
|
|
44
|
+
export const STATUS_CLASS = Object.freeze({
|
|
45
|
+
ACTIVE: "active",
|
|
46
|
+
WAITING: "waiting",
|
|
47
|
+
BLOCKED: "blocked",
|
|
48
|
+
DONE: "done",
|
|
49
|
+
UNKNOWN: "unknown",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/** Source mode values reflecting evidence availability. */
|
|
53
|
+
export const SOURCE_MODE = Object.freeze({
|
|
54
|
+
/** Both inner-loop detectors returned live facts. */
|
|
55
|
+
LIVE_DETECTOR_BACKED: "live-detector-backed",
|
|
56
|
+
/** Live detection failed for all inner loops; checkpoint-backed drill-down remains available, but the top-level state stays unknown. */
|
|
57
|
+
CHECKPOINT_ONLY: "checkpoint-only",
|
|
58
|
+
/** Degraded mode: mixed live + checkpoint fallback keeps the top-level state unknown; complete caller-supplied current-state inputs can still derive a top-level state. */
|
|
59
|
+
PARTIAL: "partial",
|
|
60
|
+
/** No live facts and no valid checkpoint available. */
|
|
61
|
+
UNAVAILABLE: "unavailable",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/** Trust classification for the inspection output. */
|
|
65
|
+
export const TRUST = Object.freeze({
|
|
66
|
+
/** All facts come from live authoritative sources. */
|
|
67
|
+
AUTHORITATIVE: "authoritative",
|
|
68
|
+
/** Facts come from a previously persisted checkpoint (advisory only). */
|
|
69
|
+
CHECKPOINT: "checkpoint",
|
|
70
|
+
/** Facts are a mix of live and checkpoint, or only partial live coverage. */
|
|
71
|
+
DEGRADED: "degraded",
|
|
72
|
+
/** No trustworthy evidence available. */
|
|
73
|
+
UNAVAILABLE: "unavailable",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Status-class mapping
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Map an outerAction value to a top-level statusClass.
|
|
82
|
+
*
|
|
83
|
+
* Mapping:
|
|
84
|
+
* continue_wait → waiting
|
|
85
|
+
* reenter_copilot_loop → active
|
|
86
|
+
* reenter_reviewer_loop → active
|
|
87
|
+
* stop → blocked
|
|
88
|
+
* done → done
|
|
89
|
+
* anything else / undefined → unknown
|
|
90
|
+
*
|
|
91
|
+
* @param {string | undefined} outerAction
|
|
92
|
+
* @returns {string} one of STATUS_CLASS values
|
|
93
|
+
*/
|
|
94
|
+
export function mapOuterActionToStatusClass(outerAction) {
|
|
95
|
+
switch (outerAction) {
|
|
96
|
+
case "continue_wait":
|
|
97
|
+
return STATUS_CLASS.WAITING;
|
|
98
|
+
case "reenter_copilot_loop":
|
|
99
|
+
case "reenter_reviewer_loop":
|
|
100
|
+
return STATUS_CLASS.ACTIVE;
|
|
101
|
+
case "stop":
|
|
102
|
+
return STATUS_CLASS.BLOCKED;
|
|
103
|
+
case "done":
|
|
104
|
+
return STATUS_CLASS.DONE;
|
|
105
|
+
default:
|
|
106
|
+
return STATUS_CLASS.UNKNOWN;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function buildReviewerScope(snapshotLike) {
|
|
111
|
+
if (snapshotLike?.reviewerScope === "single_reviewer") {
|
|
112
|
+
return {
|
|
113
|
+
mode: "single_reviewer",
|
|
114
|
+
reviewerLogin: typeof snapshotLike?.reviewerLogin === "string" ? snapshotLike.reviewerLogin : null,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
mode: "all_reviewers",
|
|
120
|
+
reviewerLogin: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const LIVE_STEERING_TERMINAL_STATES = new Set([
|
|
125
|
+
"no_pr",
|
|
126
|
+
"done",
|
|
127
|
+
"review_request_unavailable",
|
|
128
|
+
"blocked_needs_user_decision",
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
function evaluateLiveSteeringAvailability({
|
|
132
|
+
sourceMode,
|
|
133
|
+
trust,
|
|
134
|
+
markers,
|
|
135
|
+
statusClass,
|
|
136
|
+
copilotCurrentState,
|
|
137
|
+
}) {
|
|
138
|
+
if (sourceMode !== SOURCE_MODE.LIVE_DETECTOR_BACKED) {
|
|
139
|
+
return { status: "unavailable", reason: "live_steering_unavailable_source_mode" };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (trust !== TRUST.AUTHORITATIVE) {
|
|
143
|
+
return { status: "unavailable", reason: "live_steering_unavailable_trust" };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (
|
|
147
|
+
markers.missing.length > 0
|
|
148
|
+
|| markers.stale.length > 0
|
|
149
|
+
|| markers.conflicts.length > 0
|
|
150
|
+
) {
|
|
151
|
+
return { status: "unavailable", reason: "live_steering_unavailable_markers" };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (statusClass === STATUS_CLASS.UNKNOWN || typeof copilotCurrentState !== "string") {
|
|
155
|
+
return { status: "unavailable", reason: "live_steering_unavailable_unknown_state" };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
statusClass === STATUS_CLASS.DONE
|
|
160
|
+
|| statusClass === STATUS_CLASS.BLOCKED
|
|
161
|
+
|| LIVE_STEERING_TERMINAL_STATES.has(copilotCurrentState)
|
|
162
|
+
) {
|
|
163
|
+
return { status: "unavailable", reason: "live_steering_unavailable_terminal_state" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { status: "available", reason: null };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Snapshot composer
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Compose a read-only run inspection snapshot for one explicitly targeted run
|
|
175
|
+
* in the Copilot PR outer-loop family.
|
|
176
|
+
*
|
|
177
|
+
* This function is pure: it performs no I/O and does not mutate any files or
|
|
178
|
+
* checkpoints. All evidence must be gathered by the caller before invoking it.
|
|
179
|
+
*
|
|
180
|
+
* @param {object} params
|
|
181
|
+
* @param {{ repo: string, pr: number }} params.target
|
|
182
|
+
* Explicit target identity.
|
|
183
|
+
* @param {string} params.inspectedAt
|
|
184
|
+
* ISO 8601 timestamp of when the inspection was initiated.
|
|
185
|
+
* @param {string | undefined} [params.outerState]
|
|
186
|
+
* Authoritative outer-loop state derived by the caller from a complete
|
|
187
|
+
* current-state picture, or undefined when the outer state cannot be determined.
|
|
188
|
+
* @param {string[] | undefined} [params.outerAllowedTransitions]
|
|
189
|
+
* Authoritative allowed next outer states when `outerState` is available.
|
|
190
|
+
* @param {string | undefined} params.outerAction
|
|
191
|
+
* Backward-compatible outer-loop action projection derived by the caller from
|
|
192
|
+
* the authoritative outer interpretation, or undefined when the outer action
|
|
193
|
+
* cannot be determined.
|
|
194
|
+
* @param {string | undefined} [params.outerReason]
|
|
195
|
+
* Optional stop reason string from the authoritative outer-loop interpretation
|
|
196
|
+
* (for example `interpretOuterLoopState(...).stopReason`, such as
|
|
197
|
+
* "copilot_blocked").
|
|
198
|
+
* @param {{ snapshot: object, interpretation: { state: string, allowedTransitions: string[], nextAction: string } } | null} params.copilotEvidence
|
|
199
|
+
* Live copilot inner-loop facts. null when live detection was unavailable.
|
|
200
|
+
* @param {{ snapshot: object, interpretation: { state: string, allowedTransitions: string[], nextAction: string } } | null} params.reviewerEvidence
|
|
201
|
+
* Live reviewer inner-loop facts. null when live detection was unavailable.
|
|
202
|
+
* @param {object | null} params.existingCheckpoint
|
|
203
|
+
* Previously persisted outer-loop checkpoint (read-only). null when not found.
|
|
204
|
+
* @param {string | null} [params.checkpointEvidencePath]
|
|
205
|
+
* Concrete checkpoint file path used by the caller when a checkpoint was found.
|
|
206
|
+
* @param {{ copilot: "ok"|"failed", reviewer: "ok"|"failed" }} params.liveAvailability
|
|
207
|
+
* Tracks whether each detector/interpreter path succeeded ("ok") or failed ("failed").
|
|
208
|
+
* @param {{ copilot?: "live"|"input", reviewer?: "live"|"input" }} [params.evidenceSourceKinds]
|
|
209
|
+
* Indicates whether successful evidence came from live detection or caller-supplied snapshot input.
|
|
210
|
+
* @param {boolean} [params.explicitTargetMissing]
|
|
211
|
+
* True when the explicit target was not found by one or more detector inputs.
|
|
212
|
+
* @param {string | null} [params.steeringLocatorPath]
|
|
213
|
+
* Path to the steering state file, when explicitly provided by the caller.
|
|
214
|
+
* null means no locator was given.
|
|
215
|
+
* @param {object | null} [params.steeringEvidence]
|
|
216
|
+
* Loaded and normalized steering state, or null when file not found.
|
|
217
|
+
* @param {boolean} [params.steeringLoadFailed]
|
|
218
|
+
* true when a steering locator was provided but loading the file failed.
|
|
219
|
+
* @param {string | null} [params.steeringUnavailableReason]
|
|
220
|
+
* Optional explicit unavailable reason when a steering file was supplied but
|
|
221
|
+
* cannot be trusted for this inspected target.
|
|
222
|
+
* @param {object | null} [params.steeringReadback]
|
|
223
|
+
* Precomputed steering readback summary for the inspection surface.
|
|
224
|
+
* @param {object} [params.loopIterations]
|
|
225
|
+
* Best-effort Copilot remote-loop iteration summary. This is intended for
|
|
226
|
+
* GitHub-backed PR loops where durable review/timeline facts are available.
|
|
227
|
+
* @returns {object} inspection snapshot with always-present and best-effort fields
|
|
228
|
+
*/
|
|
229
|
+
export function composeRunInspectionSnapshot({
|
|
230
|
+
target,
|
|
231
|
+
inspectedAt,
|
|
232
|
+
outerState,
|
|
233
|
+
outerAllowedTransitions,
|
|
234
|
+
outerAction,
|
|
235
|
+
outerReason,
|
|
236
|
+
copilotEvidence,
|
|
237
|
+
reviewerEvidence,
|
|
238
|
+
existingCheckpoint,
|
|
239
|
+
checkpointEvidencePath = null,
|
|
240
|
+
liveAvailability,
|
|
241
|
+
evidenceSourceKinds = { copilot: "live", reviewer: "live" },
|
|
242
|
+
explicitTargetMissing = false,
|
|
243
|
+
steeringLocatorPath = null,
|
|
244
|
+
steeringEvidence = null,
|
|
245
|
+
steeringLoadFailed = false,
|
|
246
|
+
steeringUnavailableReason = null,
|
|
247
|
+
steeringReadback = null,
|
|
248
|
+
loopIterations = {
|
|
249
|
+
available: false,
|
|
250
|
+
source: "github_pr_timeline",
|
|
251
|
+
reason: "unavailable",
|
|
252
|
+
},
|
|
253
|
+
}) {
|
|
254
|
+
const { repo, pr } = target;
|
|
255
|
+
const runId = deriveRunIdForInspectionTarget(target);
|
|
256
|
+
const markers = { missing: [], stale: [], conflicts: [] };
|
|
257
|
+
|
|
258
|
+
const copilotLiveOk = liveAvailability.copilot === "ok";
|
|
259
|
+
const reviewerLiveOk = liveAvailability.reviewer === "ok";
|
|
260
|
+
const copilotLiveFailed = liveAvailability.copilot === "failed";
|
|
261
|
+
const reviewerLiveFailed = liveAvailability.reviewer === "failed";
|
|
262
|
+
const bothLiveOk = copilotLiveOk && reviewerLiveOk;
|
|
263
|
+
const copilotSourceKind = evidenceSourceKinds.copilot ?? "live";
|
|
264
|
+
const reviewerSourceKind = evidenceSourceKinds.reviewer ?? "live";
|
|
265
|
+
const bothSourceKindsLive = copilotSourceKind === "live" && reviewerSourceKind === "live";
|
|
266
|
+
const inputSnapshotMode = bothLiveOk && !bothSourceKindsLive;
|
|
267
|
+
|
|
268
|
+
const evidenceAuthoritative = [];
|
|
269
|
+
const evidenceCheckpoint = [];
|
|
270
|
+
|
|
271
|
+
// -------------------------------------------------------------------------
|
|
272
|
+
// Determine source mode and trust
|
|
273
|
+
// -------------------------------------------------------------------------
|
|
274
|
+
|
|
275
|
+
let sourceMode;
|
|
276
|
+
let trust;
|
|
277
|
+
|
|
278
|
+
if (explicitTargetMissing) {
|
|
279
|
+
sourceMode = SOURCE_MODE.UNAVAILABLE;
|
|
280
|
+
trust = TRUST.UNAVAILABLE;
|
|
281
|
+
markers.missing.push("explicit target PR was not found");
|
|
282
|
+
} else if (bothLiveOk && bothSourceKindsLive) {
|
|
283
|
+
sourceMode = SOURCE_MODE.LIVE_DETECTOR_BACKED;
|
|
284
|
+
trust = TRUST.AUTHORITATIVE;
|
|
285
|
+
evidenceAuthoritative.push("live Copilot loop detector", "live reviewer loop detector");
|
|
286
|
+
} else if (inputSnapshotMode) {
|
|
287
|
+
sourceMode = SOURCE_MODE.PARTIAL;
|
|
288
|
+
trust = TRUST.DEGRADED;
|
|
289
|
+
} else if (!copilotLiveOk && !reviewerLiveOk) {
|
|
290
|
+
if (existingCheckpoint !== null && typeof existingCheckpoint?.outerAction === "string") {
|
|
291
|
+
sourceMode = SOURCE_MODE.CHECKPOINT_ONLY;
|
|
292
|
+
trust = TRUST.CHECKPOINT;
|
|
293
|
+
} else {
|
|
294
|
+
sourceMode = SOURCE_MODE.UNAVAILABLE;
|
|
295
|
+
trust = TRUST.UNAVAILABLE;
|
|
296
|
+
}
|
|
297
|
+
if (copilotLiveFailed) {
|
|
298
|
+
markers.missing.push("live Copilot loop state (detection failed)");
|
|
299
|
+
}
|
|
300
|
+
if (reviewerLiveFailed) {
|
|
301
|
+
markers.missing.push("live reviewer loop state (detection failed)");
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
// Partial: one live ok, one failed
|
|
305
|
+
sourceMode = SOURCE_MODE.PARTIAL;
|
|
306
|
+
trust = TRUST.DEGRADED;
|
|
307
|
+
if (copilotLiveOk) {
|
|
308
|
+
evidenceAuthoritative.push("live Copilot loop detector");
|
|
309
|
+
} else {
|
|
310
|
+
if (copilotLiveFailed) {
|
|
311
|
+
markers.missing.push("live Copilot loop state (detection failed)");
|
|
312
|
+
}
|
|
313
|
+
if (existingCheckpoint?.copilotState !== undefined) {
|
|
314
|
+
markers.stale.push("copilot loop state (checkpoint-derived; live detection failed)");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (reviewerLiveOk) {
|
|
318
|
+
evidenceAuthoritative.push("live reviewer loop detector");
|
|
319
|
+
} else {
|
|
320
|
+
if (reviewerLiveFailed) {
|
|
321
|
+
markers.missing.push("live reviewer loop state (detection failed)");
|
|
322
|
+
}
|
|
323
|
+
if (existingCheckpoint?.reviewerState !== undefined) {
|
|
324
|
+
markers.stale.push("reviewer loop state (checkpoint-derived; live detection failed)");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// -------------------------------------------------------------------------
|
|
330
|
+
// Checkpoint evidence and conflict detection
|
|
331
|
+
// -------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
if (existingCheckpoint !== null) {
|
|
334
|
+
if (checkpointEvidencePath !== null) {
|
|
335
|
+
evidenceCheckpoint.push(checkpointEvidencePath);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (bothLiveOk && outerAction !== undefined) {
|
|
339
|
+
// Check for conflicts between live-derived action and checkpoint
|
|
340
|
+
const ckptOuterAction = existingCheckpoint.outerAction;
|
|
341
|
+
if (typeof ckptOuterAction === "string" && ckptOuterAction !== outerAction) {
|
|
342
|
+
markers.conflicts.push(
|
|
343
|
+
`checkpoint outerAction '${ckptOuterAction}' differs from live-derived '${outerAction}'`,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const ckptCopilotState = existingCheckpoint.copilotState;
|
|
348
|
+
if (
|
|
349
|
+
copilotEvidence !== null
|
|
350
|
+
&& typeof ckptCopilotState === "string"
|
|
351
|
+
&& ckptCopilotState !== copilotEvidence.interpretation.state
|
|
352
|
+
) {
|
|
353
|
+
markers.conflicts.push(
|
|
354
|
+
`checkpoint copilotState '${ckptCopilotState}' differs from live '${copilotEvidence.interpretation.state}'`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const ckptReviewerState = existingCheckpoint.reviewerState;
|
|
359
|
+
if (
|
|
360
|
+
reviewerEvidence !== null
|
|
361
|
+
&& typeof ckptReviewerState === "string"
|
|
362
|
+
&& ckptReviewerState !== reviewerEvidence.interpretation.state
|
|
363
|
+
) {
|
|
364
|
+
markers.conflicts.push(
|
|
365
|
+
`checkpoint reviewerState '${ckptReviewerState}' differs from live '${reviewerEvidence.interpretation.state}'`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// -------------------------------------------------------------------------
|
|
372
|
+
// Top-level outer-state / outerAction surfacing eligibility
|
|
373
|
+
// -------------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
// Top-level outer fields are only surfaced when the caller derived them from
|
|
376
|
+
// a complete current evidence set. Checkpoint-backed or mixed fallback remains
|
|
377
|
+
// available only as advisory drill-down evidence in this chunk.
|
|
378
|
+
const effectiveOuterState = isKnownOuterState(outerState) ? outerState : undefined;
|
|
379
|
+
const effectiveOuterAllowedTransitions = effectiveOuterState !== undefined && Array.isArray(outerAllowedTransitions)
|
|
380
|
+
? [...outerAllowedTransitions]
|
|
381
|
+
: undefined;
|
|
382
|
+
const effectiveOuterAction = outerAction;
|
|
383
|
+
const effectiveOuterReason = outerReason;
|
|
384
|
+
|
|
385
|
+
// -------------------------------------------------------------------------
|
|
386
|
+
// Determine statusClass
|
|
387
|
+
// -------------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
let statusClass;
|
|
390
|
+
if (sourceMode === SOURCE_MODE.UNAVAILABLE || effectiveOuterAction === undefined) {
|
|
391
|
+
statusClass = STATUS_CLASS.UNKNOWN;
|
|
392
|
+
} else {
|
|
393
|
+
statusClass = mapOuterActionToStatusClass(effectiveOuterAction);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// -------------------------------------------------------------------------
|
|
397
|
+
// Determine needsAttention
|
|
398
|
+
// -------------------------------------------------------------------------
|
|
399
|
+
|
|
400
|
+
const needsAttention =
|
|
401
|
+
effectiveOuterAction === "stop"
|
|
402
|
+
|| markers.conflicts.length > 0
|
|
403
|
+
|| markers.missing.length > 0
|
|
404
|
+
|| sourceMode === SOURCE_MODE.CHECKPOINT_ONLY
|
|
405
|
+
|| sourceMode === SOURCE_MODE.UNAVAILABLE
|
|
406
|
+
|| (sourceMode === SOURCE_MODE.PARTIAL && !inputSnapshotMode);
|
|
407
|
+
|
|
408
|
+
// -------------------------------------------------------------------------
|
|
409
|
+
// Build evidence summary
|
|
410
|
+
// -------------------------------------------------------------------------
|
|
411
|
+
|
|
412
|
+
let evidenceSummary;
|
|
413
|
+
if (explicitTargetMissing) {
|
|
414
|
+
evidenceSummary = "The explicit target PR was not found; no current run state could be determined.";
|
|
415
|
+
} else if (sourceMode === SOURCE_MODE.LIVE_DETECTOR_BACKED) {
|
|
416
|
+
if (effectiveOuterState === "stay_with_current_live_owner") {
|
|
417
|
+
evidenceSummary = "Live detectors agree a live owner already controls this run, so the orchestrator should not issue a new handoff yet.";
|
|
418
|
+
} else if (effectiveOuterState === "needs_reconcile") {
|
|
419
|
+
evidenceSummary = "Live detectors found ambiguous or conflicting state, so the orchestrator must reconcile before continuing.";
|
|
420
|
+
} else if (effectiveOuterState === "stop_needs_human") {
|
|
421
|
+
evidenceSummary = `Live detectors indicate a blocked outer state that needs human intervention${effectiveOuterReason !== undefined ? ` (reason: ${effectiveOuterReason})` : ""}.`;
|
|
422
|
+
} else if (effectiveOuterState === "done_terminal") {
|
|
423
|
+
evidenceSummary = "Live detectors agree the PR is complete.";
|
|
424
|
+
} else if (effectiveOuterState === "continue_current_wait") {
|
|
425
|
+
evidenceSummary = "Live detectors agree the orchestrator is in its durable wait state.";
|
|
426
|
+
} else if (effectiveOuterState === "handoff_to_copilot_loop") {
|
|
427
|
+
evidenceSummary = "Live detectors indicate the next meaningful work belongs to the Copilot loop.";
|
|
428
|
+
} else if (effectiveOuterState === "handoff_to_reviewer_loop") {
|
|
429
|
+
evidenceSummary = "Live detectors indicate the next meaningful work belongs to the reviewer loop.";
|
|
430
|
+
} else if (effectiveOuterAction !== undefined) {
|
|
431
|
+
evidenceSummary = `Live detectors returned results, but only the compatibility outerAction could be determined (outerAction: ${effectiveOuterAction}).`;
|
|
432
|
+
} else {
|
|
433
|
+
evidenceSummary = "Live detectors returned results but outer state could not be determined.";
|
|
434
|
+
}
|
|
435
|
+
if (markers.conflicts.length > 0) {
|
|
436
|
+
evidenceSummary += " Checkpoint state conflicts with live facts.";
|
|
437
|
+
}
|
|
438
|
+
} else if (sourceMode === SOURCE_MODE.CHECKPOINT_ONLY) {
|
|
439
|
+
evidenceSummary =
|
|
440
|
+
"No live detector facts are available. Checkpoint state is shown as advisory drill-down only, and the current top-level run state could not be confirmed.";
|
|
441
|
+
} else if (sourceMode === SOURCE_MODE.PARTIAL) {
|
|
442
|
+
evidenceSummary = inputSnapshotMode
|
|
443
|
+
? "One or more caller-supplied snapshot inputs were used. The result reflects the complete current-state picture provided to inspection, but remains degraded because it was not fully live-detector-backed."
|
|
444
|
+
: "Only partial live evidence is available. Any checkpoint-backed state is advisory only, so the current top-level run state could not be confirmed.";
|
|
445
|
+
} else {
|
|
446
|
+
evidenceSummary = "No evidence available to determine current run state.";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// -------------------------------------------------------------------------
|
|
450
|
+
// Build layers (best-effort drill-down)
|
|
451
|
+
// -------------------------------------------------------------------------
|
|
452
|
+
|
|
453
|
+
const layers = {};
|
|
454
|
+
|
|
455
|
+
if (copilotLiveOk && copilotEvidence !== null) {
|
|
456
|
+
const copilotSummary = summarizeLoopInterpretation(copilotEvidence.interpretation);
|
|
457
|
+
layers.copilot = {
|
|
458
|
+
currentState: copilotEvidence.interpretation.state,
|
|
459
|
+
allowedTransitions: copilotEvidence.interpretation.allowedTransitions,
|
|
460
|
+
sameHeadCleanConverged: copilotEvidence.interpretation.sameHeadCleanConverged === true,
|
|
461
|
+
loopDisposition: copilotSummary.loopDisposition,
|
|
462
|
+
terminal: copilotSummary.terminal,
|
|
463
|
+
};
|
|
464
|
+
} else if (typeof existingCheckpoint?.copilotState === "string") {
|
|
465
|
+
layers.copilot = {
|
|
466
|
+
currentState: existingCheckpoint.copilotState,
|
|
467
|
+
source: "checkpoint",
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (reviewerLiveOk && reviewerEvidence !== null) {
|
|
472
|
+
const reviewerSubmittedReviewState = reviewerEvidence.snapshot.submittedReviewState ?? null;
|
|
473
|
+
const reviewerApprovedOnCurrentHead = reviewerEvidence.snapshot.submittedReviewPresent === true
|
|
474
|
+
&& reviewerSubmittedReviewState === "APPROVED"
|
|
475
|
+
&& reviewerEvidence.snapshot.prHeadSha !== null
|
|
476
|
+
&& reviewerEvidence.snapshot.submittedReviewCommitSha !== null
|
|
477
|
+
&& reviewerEvidence.snapshot.prHeadSha === reviewerEvidence.snapshot.submittedReviewCommitSha;
|
|
478
|
+
|
|
479
|
+
layers.reviewer = {
|
|
480
|
+
currentState: reviewerEvidence.interpretation.state,
|
|
481
|
+
allowedTransitions: reviewerEvidence.interpretation.allowedTransitions,
|
|
482
|
+
scope: buildReviewerScope(reviewerEvidence.snapshot),
|
|
483
|
+
submittedReviewState: reviewerSubmittedReviewState,
|
|
484
|
+
approvedOnCurrentHead: reviewerApprovedOnCurrentHead,
|
|
485
|
+
};
|
|
486
|
+
} else if (typeof existingCheckpoint?.reviewerState === "string") {
|
|
487
|
+
layers.reviewer = {
|
|
488
|
+
currentState: existingCheckpoint.reviewerState,
|
|
489
|
+
source: "checkpoint",
|
|
490
|
+
scope: buildReviewerScope(existingCheckpoint),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Steering layer (best-effort; only when an explicit locator is provided)
|
|
495
|
+
if (steeringLocatorPath === null || steeringLocatorPath === undefined) {
|
|
496
|
+
layers.steering = {
|
|
497
|
+
status: "unavailable",
|
|
498
|
+
reason: "no_steering_locator",
|
|
499
|
+
};
|
|
500
|
+
} else if (steeringLoadFailed) {
|
|
501
|
+
layers.steering = {
|
|
502
|
+
status: "unavailable",
|
|
503
|
+
reason: "load_failed",
|
|
504
|
+
};
|
|
505
|
+
} else if (steeringUnavailableReason !== null) {
|
|
506
|
+
layers.steering = {
|
|
507
|
+
status: "unavailable",
|
|
508
|
+
reason: steeringUnavailableReason,
|
|
509
|
+
};
|
|
510
|
+
} else if (steeringEvidence === null) {
|
|
511
|
+
layers.steering = {
|
|
512
|
+
status: "unavailable",
|
|
513
|
+
reason: "no_steering_file",
|
|
514
|
+
};
|
|
515
|
+
} else {
|
|
516
|
+
const liveSteering = evaluateLiveSteeringAvailability({
|
|
517
|
+
sourceMode,
|
|
518
|
+
trust,
|
|
519
|
+
markers,
|
|
520
|
+
statusClass,
|
|
521
|
+
copilotCurrentState: layers.copilot?.currentState,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
if (liveSteering.status === "available") {
|
|
525
|
+
layers.steering = {
|
|
526
|
+
status: "available",
|
|
527
|
+
...(steeringReadback ?? {}),
|
|
528
|
+
liveSteering,
|
|
529
|
+
};
|
|
530
|
+
} else {
|
|
531
|
+
layers.steering = {
|
|
532
|
+
status: "unavailable",
|
|
533
|
+
reason: liveSteering.reason,
|
|
534
|
+
liveSteering,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// -------------------------------------------------------------------------
|
|
540
|
+
// Lifecycle phase resolution (best-effort from available PR facts)
|
|
541
|
+
// -------------------------------------------------------------------------
|
|
542
|
+
|
|
543
|
+
let lifecyclePhase = null;
|
|
544
|
+
let lifecycleAllowedTransitions = null;
|
|
545
|
+
|
|
546
|
+
if (copilotLiveOk && copilotEvidence !== null) {
|
|
547
|
+
const copilotState = copilotEvidence.interpretation.state;
|
|
548
|
+
const mappedPhase = lifecyclePhaseForCopilotState(copilotState);
|
|
549
|
+
if (mappedPhase) {
|
|
550
|
+
lifecyclePhase = mappedPhase;
|
|
551
|
+
lifecycleAllowedTransitions = getAllowedTransitions(mappedPhase);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (lifecyclePhase === null) {
|
|
556
|
+
// Fallback: derive from available PR facts
|
|
557
|
+
const loopIter = loopIterations ?? {};
|
|
558
|
+
const hasUnresolvedThreads = typeof loopIter.unresolvedReviewThreads === "number"
|
|
559
|
+
&& loopIter.unresolvedReviewThreads > 0;
|
|
560
|
+
const copilotState = copilotLiveOk && copilotEvidence !== null
|
|
561
|
+
? copilotEvidence.interpretation.state
|
|
562
|
+
: null;
|
|
563
|
+
const prIsDraft = copilotState === "pr_draft";
|
|
564
|
+
|
|
565
|
+
const resolved = resolveLifecycleState({
|
|
566
|
+
hasLinkedPr: !explicitTargetMissing,
|
|
567
|
+
prIsDraft,
|
|
568
|
+
hasUnresolvedThreads,
|
|
569
|
+
});
|
|
570
|
+
lifecyclePhase = resolved.state;
|
|
571
|
+
lifecycleAllowedTransitions = resolved.allowedTransitions;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// -------------------------------------------------------------------------
|
|
575
|
+
// Assemble final snapshot
|
|
576
|
+
// -------------------------------------------------------------------------
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
ok: true,
|
|
580
|
+
schemaVersion: SCHEMA_VERSION,
|
|
581
|
+
target: { repo, pr },
|
|
582
|
+
runId,
|
|
583
|
+
inspectedAt,
|
|
584
|
+
activeStateFamily: ACTIVE_STATE_FAMILY,
|
|
585
|
+
outerState: effectiveOuterState ?? "unknown",
|
|
586
|
+
...(effectiveOuterAllowedTransitions !== undefined ? { allowedTransitions: effectiveOuterAllowedTransitions } : {}),
|
|
587
|
+
outerAction: effectiveOuterAction ?? "unknown",
|
|
588
|
+
activeFamilyState: effectiveOuterAction ?? "unknown",
|
|
589
|
+
statusClass,
|
|
590
|
+
needsAttention,
|
|
591
|
+
sourceMode,
|
|
592
|
+
trust,
|
|
593
|
+
evidence: {
|
|
594
|
+
summary: evidenceSummary,
|
|
595
|
+
authoritative: evidenceAuthoritative,
|
|
596
|
+
checkpoint: evidenceCheckpoint,
|
|
597
|
+
},
|
|
598
|
+
markers,
|
|
599
|
+
loopIterations,
|
|
600
|
+
layers,
|
|
601
|
+
lifecyclePhase,
|
|
602
|
+
lifecycleAllowedTransitions,
|
|
603
|
+
};
|
|
604
|
+
}
|