@effect-agent/pr-review 0.1.0-beta.24 → 0.1.0-beta.25
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/README.md +83 -202
- package/dist/action.d.mts +25 -16
- package/dist/action.mjs +51 -39
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/{fan-out-Bi1v0VaU.d.mts → fan-out-CMEsbFLk.d.mts} +432 -206
- package/dist/{github-C6jrBLA2.mjs → github-NjgxGqwM.mjs} +2141 -1658
- package/dist/github-NjgxGqwM.mjs.map +1 -0
- package/dist/index.d.mts +20 -12
- package/dist/index.mjs +3 -3
- package/dist/{providers-2Jao2ZAX.mjs → providers-CODZQCmL.mjs} +192 -79
- package/dist/providers-CODZQCmL.mjs.map +1 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/src/action.ts +123 -78
- package/src/cli.ts +6 -1
- package/src/index.ts +1 -0
- package/src/internal/adjudication.ts +415 -0
- package/src/internal/coverage.ts +41 -60
- package/src/internal/fan-out.ts +56 -4
- package/src/internal/github-env.ts +9 -0
- package/src/internal/github.ts +207 -0
- package/src/internal/progress.ts +1 -1
- package/src/internal/render.ts +186 -42
- package/src/internal/retirement.ts +16 -17
- package/src/internal/review-agent.ts +39 -4
- package/src/internal/review-state.ts +161 -42
- package/src/internal/review-units.ts +10 -9
- package/src/internal/run.ts +178 -64
- package/dist/github-C6jrBLA2.mjs.map +0 -1
- package/dist/providers-2Jao2ZAX.mjs.map +0 -1
package/src/internal/run.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect,
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
2
|
import {
|
|
3
3
|
makeUsageBudget,
|
|
4
4
|
toRunBudgetHook,
|
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
} from "effect-agent";
|
|
10
10
|
import { type Tool } from "effect/unstable/ai";
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
collectReviewAdjudications,
|
|
14
|
+
renderAdjudicationContextLine,
|
|
15
|
+
renderPriorFindingContextLine,
|
|
16
|
+
buildPriorReviewContext,
|
|
17
|
+
} from "./adjudication.ts";
|
|
12
18
|
import {
|
|
13
19
|
assessFlatReview,
|
|
14
|
-
compatibilityCoverage,
|
|
15
20
|
fanOutInputCoverage,
|
|
16
21
|
ReviewAssurance,
|
|
17
|
-
ReviewCoverage,
|
|
18
22
|
ReviewInputCoverage,
|
|
19
23
|
} from "./coverage.ts";
|
|
20
24
|
import type { ChangedFile } from "./diff.ts";
|
|
@@ -30,17 +34,21 @@ import {
|
|
|
30
34
|
ReviewMission,
|
|
31
35
|
} from "./review-agent.ts";
|
|
32
36
|
import {
|
|
37
|
+
adjudicationIdentity,
|
|
38
|
+
concernIdentity,
|
|
39
|
+
findingIdentity,
|
|
33
40
|
fromStoredConcern,
|
|
34
41
|
fromStoredFinding,
|
|
35
42
|
MAX_STORED_UNREVIEWED_PASSES,
|
|
36
43
|
MAX_STORED_UNREVIEWED_PATHS,
|
|
37
44
|
ReviewExecutionContext,
|
|
38
45
|
ReviewState,
|
|
46
|
+
StoredAdjudication,
|
|
39
47
|
StoredUnreviewedPass,
|
|
40
48
|
toStoredConcern,
|
|
41
49
|
toStoredFinding,
|
|
42
50
|
} from "./review-state.ts";
|
|
43
|
-
import { rankAndDedupeConcerns, rankAndDedupeFindings } from "./review-units.ts";
|
|
51
|
+
import { rankAndDedupeConcerns, rankAndDedupeFindings, reviewConcernKey } from "./review-units.ts";
|
|
44
52
|
import { PullRequestSource, type PullRequestMetadata } from "./source.ts";
|
|
45
53
|
|
|
46
54
|
// ---------------------------------------------------------------------------
|
|
@@ -91,8 +99,6 @@ export class ReviewRunOutcome extends Schema.Class<ReviewRunOutcome>(
|
|
|
91
99
|
activeFindings: Schema.Array(ReviewFinding).check(Schema.isMaxLength(20)),
|
|
92
100
|
/** All currently unresolved concerns, including concerns carried to final audit. */
|
|
93
101
|
activeConcerns: Schema.Array(ReviewConcern).check(Schema.isMaxLength(10)),
|
|
94
|
-
/** Host-owned structural coverage used by the Actions check conclusion. */
|
|
95
|
-
coverage: ReviewCoverage,
|
|
96
102
|
/** Exact path/evidence assignment, distinct from semantic review work. */
|
|
97
103
|
inputCoverage: ReviewInputCoverage,
|
|
98
104
|
/** Settlement of scheduled discovery, specialist, and verification work. */
|
|
@@ -110,6 +116,8 @@ export class ReviewRunOutcome extends Schema.Class<ReviewRunOutcome>(
|
|
|
110
116
|
reviewMode: Schema.optionalKey(Schema.Literals(["incremental", "full"])),
|
|
111
117
|
reviewReason: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(1_000))),
|
|
112
118
|
state: Schema.optionalKey(ReviewState),
|
|
119
|
+
/** Maintainer adjudications standing against this run's identities. */
|
|
120
|
+
adjudications: Schema.optionalKey(Schema.Array(StoredAdjudication).check(Schema.isMaxLength(20))),
|
|
113
121
|
}) {}
|
|
114
122
|
|
|
115
123
|
export interface ExecuteReviewOptions {
|
|
@@ -137,10 +145,19 @@ export interface ExecuteReviewOptions {
|
|
|
137
145
|
readonly runUrl?: string | undefined;
|
|
138
146
|
}
|
|
139
147
|
|
|
140
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Build the mission one review run frames from the source's snapshot. The
|
|
150
|
+
* optional continuity context (adjudicated identities, prior-round findings
|
|
151
|
+
* on re-reviewed scope) reaches only RUN missions — fingerprint missions stay
|
|
152
|
+
* plain so an adjudication never invalidates skip-unchanged authority.
|
|
153
|
+
*/
|
|
141
154
|
export const buildReviewMission = (
|
|
142
155
|
metadata: PullRequestMetadata,
|
|
143
156
|
files: ReadonlyArray<ChangedFile>,
|
|
157
|
+
context?: {
|
|
158
|
+
readonly adjudicated?: ReadonlyArray<string> | undefined;
|
|
159
|
+
readonly priorFindings?: ReadonlyArray<string> | undefined;
|
|
160
|
+
},
|
|
144
161
|
): ReviewMission =>
|
|
145
162
|
ReviewMission.make({
|
|
146
163
|
repository: metadata.repository,
|
|
@@ -150,6 +167,12 @@ export const buildReviewMission = (
|
|
|
150
167
|
baseRef: metadata.baseRef,
|
|
151
168
|
headRef: metadata.headRef,
|
|
152
169
|
changedFileCount: files.length,
|
|
170
|
+
...(context?.adjudicated !== undefined && context.adjudicated.length > 0
|
|
171
|
+
? { adjudicatedContext: context.adjudicated.slice(0, 20) }
|
|
172
|
+
: {}),
|
|
173
|
+
...(context?.priorFindings !== undefined && context.priorFindings.length > 0
|
|
174
|
+
? { priorFindingContext: context.priorFindings.slice(0, 20) }
|
|
175
|
+
: {}),
|
|
153
176
|
});
|
|
154
177
|
|
|
155
178
|
/** Enforce the configured findings bound on an already-validated review. */
|
|
@@ -167,6 +190,25 @@ export const enforceFindingsBound = (review: CodeReview, maxFindings: number): C
|
|
|
167
190
|
const findingKey = (finding: ReviewFinding): string =>
|
|
168
191
|
`${finding.path}\u0000${finding.startLine}\u0000${finding.endLine}\u0000${finding.severity}\u0000${finding.title}`;
|
|
169
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Continuity inputs resolved BEFORE any model work: the standing maintainer
|
|
195
|
+
* adjudications (fresh host listing merged later-wins over the prior state's
|
|
196
|
+
* stored set) and the prior-round findings whose paths this run re-reviews.
|
|
197
|
+
* The latter are dropped from the carry (the new round re-decides them) but
|
|
198
|
+
* injected as prompt context so successive rounds do not silently contradict
|
|
199
|
+
* each other — context ONLY, never auto-carried into active findings.
|
|
200
|
+
*/
|
|
201
|
+
const resolveReviewContinuityContext = Effect.fn("resolveReviewContinuityContext")(function* () {
|
|
202
|
+
const executionContext = yield* ReviewExecutionContext;
|
|
203
|
+
const priorState =
|
|
204
|
+
executionContext.mode === "incremental" ? executionContext.priorState : undefined;
|
|
205
|
+
const adjudications = yield* collectReviewAdjudications(priorState?.adjudications ?? []);
|
|
206
|
+
const affectedPaths = new Set(executionContext.affectedPaths);
|
|
207
|
+
const priorFindingsOnScope =
|
|
208
|
+
priorState?.unresolvedFindings.filter((finding) => affectedPaths.has(finding.path)) ?? [];
|
|
209
|
+
return { adjudications, priorFindingsOnScope };
|
|
210
|
+
});
|
|
211
|
+
|
|
170
212
|
/** One shape-specific review result, before the shared settlement tail. */
|
|
171
213
|
interface ReviewCore {
|
|
172
214
|
readonly review: CodeReview;
|
|
@@ -192,76 +234,137 @@ const settleReviewRun = (
|
|
|
192
234
|
readonly anchorFiles: ReadonlyArray<ChangedFile>;
|
|
193
235
|
readonly fingerprint: string | undefined;
|
|
194
236
|
readonly usage: UsageTotals | undefined;
|
|
237
|
+
readonly adjudications?: ReadonlyArray<StoredAdjudication> | undefined;
|
|
195
238
|
},
|
|
196
239
|
options: ExecuteReviewOptions,
|
|
197
240
|
) =>
|
|
198
241
|
Effect.gen(function* () {
|
|
199
242
|
const { metadata, files, anchorFiles, fingerprint, usage } = context;
|
|
200
|
-
const executionContext =
|
|
201
|
-
|
|
202
|
-
);
|
|
203
|
-
const
|
|
243
|
+
const executionContext = yield* ReviewExecutionContext;
|
|
244
|
+
const adjudications = context.adjudications ?? [];
|
|
245
|
+
const adjudicatedIdentities = new Set(adjudications.map(adjudicationIdentity));
|
|
246
|
+
const isAdjudicatedFinding = (finding: ReviewFinding): boolean =>
|
|
247
|
+
adjudicatedIdentities.has(findingIdentity(finding));
|
|
248
|
+
const isAdjudicatedConcern = (concern: ReviewConcern): boolean =>
|
|
249
|
+
adjudicatedIdentities.has(concernIdentity(concern));
|
|
250
|
+
// Suppress adjudicated model output before any ranking or bounding. A
|
|
251
|
+
// suppressed blocker must never consume the slot of an active finding.
|
|
252
|
+
const filteredReview =
|
|
253
|
+
adjudicatedIdentities.size === 0
|
|
254
|
+
? core.review
|
|
255
|
+
: CodeReview.make({
|
|
256
|
+
summary: core.review.summary,
|
|
257
|
+
verdict: core.review.verdict,
|
|
258
|
+
findings: core.review.findings.filter((finding) => !isAdjudicatedFinding(finding)),
|
|
259
|
+
...(core.review.concerns === undefined
|
|
260
|
+
? {}
|
|
261
|
+
: {
|
|
262
|
+
concerns: core.review.concerns.filter(
|
|
263
|
+
(concern) => !isAdjudicatedConcern(concern),
|
|
264
|
+
),
|
|
265
|
+
}),
|
|
266
|
+
...(core.review.walkthrough === undefined
|
|
267
|
+
? {}
|
|
268
|
+
: { walkthrough: core.review.walkthrough }),
|
|
269
|
+
});
|
|
270
|
+
const reviewPaths = new Set(files.map((file) => file.path));
|
|
271
|
+
const normalizedReview = CodeReview.make({
|
|
272
|
+
...filteredReview,
|
|
273
|
+
...(filteredReview.concerns === undefined
|
|
274
|
+
? {}
|
|
275
|
+
: {
|
|
276
|
+
concerns: filteredReview.concerns.map((concern) => {
|
|
277
|
+
const evidencePaths = concern.evidencePaths;
|
|
278
|
+
if (
|
|
279
|
+
evidencePaths === undefined ||
|
|
280
|
+
evidencePaths.some((path) => !reviewPaths.has(path))
|
|
281
|
+
) {
|
|
282
|
+
const { evidencePaths: _invalid, ...pathless } = concern;
|
|
283
|
+
return ReviewConcern.make(pathless);
|
|
284
|
+
}
|
|
285
|
+
return ReviewConcern.make({
|
|
286
|
+
...concern,
|
|
287
|
+
evidencePaths: [...new Set(evidencePaths)].sort(),
|
|
288
|
+
});
|
|
289
|
+
}),
|
|
290
|
+
}),
|
|
291
|
+
});
|
|
292
|
+
// Adjudicated identities leave the published review entirely: no inline
|
|
293
|
+
// comment, no severity count, no verdict influence — they render only in
|
|
294
|
+
// the plan's collapsed adjudicated section. Only identity-equal items are
|
|
295
|
+
// suppressed; a materially different finding at the same location (a
|
|
296
|
+
// different title) is untouched.
|
|
297
|
+
const review = enforceFindingsBound(normalizedReview, clampMaxFindings(options.maxFindings));
|
|
204
298
|
const { inputCoverage, assurance } = core;
|
|
205
299
|
const unreviewedPaths = [...new Set(core.unreviewedPaths)].sort();
|
|
206
|
-
const reviewTotalFiles = executionContext
|
|
207
|
-
const affectedPaths = new Set(
|
|
208
|
-
executionContext?.affectedPaths ??
|
|
209
|
-
files.flatMap((file) =>
|
|
210
|
-
file.previousPath === undefined ? [file.path] : [file.path, file.previousPath],
|
|
211
|
-
),
|
|
212
|
-
);
|
|
300
|
+
const reviewTotalFiles = executionContext.totalFiles;
|
|
301
|
+
const affectedPaths = new Set(executionContext.affectedPaths);
|
|
213
302
|
const priorState =
|
|
214
|
-
executionContext
|
|
303
|
+
executionContext.mode === "incremental" ? executionContext.priorState : undefined;
|
|
215
304
|
const carriedCandidates =
|
|
216
305
|
priorState?.unresolvedFindings
|
|
217
306
|
.filter((finding) => !affectedPaths.has(finding.path))
|
|
218
307
|
.map(fromStoredFinding) ?? [];
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
clampMaxFindings(options.maxFindings),
|
|
308
|
+
const eligibleCarriedCandidates = carriedCandidates.filter(
|
|
309
|
+
(finding) => !isAdjudicatedFinding(finding),
|
|
222
310
|
);
|
|
311
|
+
const activeFindings = rankAndDedupeFindings([
|
|
312
|
+
...eligibleCarriedCandidates,
|
|
313
|
+
...review.findings.filter((finding) => !isAdjudicatedFinding(finding)),
|
|
314
|
+
]).slice(0, clampMaxFindings(options.maxFindings));
|
|
223
315
|
const activeFindingKeys = new Set(activeFindings.map(findingKey));
|
|
224
316
|
const currentFindingKeys = new Set(review.findings.map(findingKey));
|
|
225
|
-
const carriedFindings =
|
|
317
|
+
const carriedFindings = eligibleCarriedCandidates.filter(
|
|
226
318
|
(finding) =>
|
|
227
319
|
activeFindingKeys.has(findingKey(finding)) && !currentFindingKeys.has(findingKey(finding)),
|
|
228
320
|
);
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
|
|
321
|
+
// A concern remains active only while every host-validated evidence path
|
|
322
|
+
// is unchanged. Touching or removing any one invalidates the old claim;
|
|
323
|
+
// range selection reopens its remaining current paths for fresh context.
|
|
324
|
+
const carriedConcernCandidates =
|
|
325
|
+
priorState?.unresolvedConcerns
|
|
326
|
+
.filter(
|
|
327
|
+
(concern) =>
|
|
328
|
+
concern.evidencePaths !== undefined &&
|
|
329
|
+
concern.evidencePaths.every((path) => !affectedPaths.has(path)),
|
|
330
|
+
)
|
|
331
|
+
.map(fromStoredConcern) ?? [];
|
|
332
|
+
const eligibleCarriedConcernCandidates = carriedConcernCandidates.filter(
|
|
333
|
+
(concern) => !isAdjudicatedConcern(concern),
|
|
334
|
+
);
|
|
232
335
|
const activeConcerns = rankAndDedupeConcerns([
|
|
233
|
-
...
|
|
234
|
-
...(review.concerns ?? []),
|
|
336
|
+
...eligibleCarriedConcernCandidates,
|
|
337
|
+
...(review.concerns ?? []).filter((concern) => !isAdjudicatedConcern(concern)),
|
|
235
338
|
]);
|
|
236
|
-
const currentConcernKeys = new Set(
|
|
237
|
-
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
activeConcerns.map((concern) => `${concern.title}\u0000${concern.body}`),
|
|
241
|
-
);
|
|
242
|
-
const carriedConcerns = carriedConcernCandidates.filter((concern) => {
|
|
243
|
-
const key = `${concern.title}\u0000${concern.body}`;
|
|
339
|
+
const currentConcernKeys = new Set((review.concerns ?? []).map(reviewConcernKey));
|
|
340
|
+
const activeConcernKeys = new Set(activeConcerns.map(reviewConcernKey));
|
|
341
|
+
const carriedConcerns = eligibleCarriedConcernCandidates.filter((concern) => {
|
|
342
|
+
const key = reviewConcernKey(concern);
|
|
244
343
|
return activeConcernKeys.has(key) && !currentConcernKeys.has(key);
|
|
245
344
|
});
|
|
246
345
|
const settled =
|
|
247
346
|
inputCoverage.status === "complete" &&
|
|
248
347
|
assurance.status !== "incomplete" &&
|
|
249
348
|
unreviewedPaths.length === 0;
|
|
349
|
+
const concernsHaveEvidencePaths = activeConcerns.every(
|
|
350
|
+
(concern) => concern.evidencePaths !== undefined,
|
|
351
|
+
);
|
|
250
352
|
// The fingerprint marker is standalone skip authority for fingerprint-only
|
|
251
353
|
// harnesses, so it is embedded only for a fully settled run.
|
|
252
|
-
const skipFingerprint = settled ? fingerprint : undefined;
|
|
354
|
+
const skipFingerprint = settled && concernsHaveEvidencePaths ? fingerprint : undefined;
|
|
253
355
|
const carriedScopeFits = unreviewedPaths.length <= MAX_STORED_UNREVIEWED_PATHS;
|
|
254
356
|
const stateCandidate =
|
|
255
|
-
executionContext !== undefined &&
|
|
256
357
|
fingerprint !== undefined &&
|
|
358
|
+
executionContext.profileFingerprint !== undefined &&
|
|
257
359
|
metadata.baseSha !== undefined &&
|
|
258
360
|
// The fingerprint and stored baseline describe the FULL pull-request
|
|
259
361
|
// surface; a truncated anchor surface cannot make either claim.
|
|
260
362
|
anchorFiles.length >= metadata.totalChangedFiles &&
|
|
261
363
|
carriedScopeFits &&
|
|
364
|
+
concernsHaveEvidencePaths &&
|
|
262
365
|
executionContext.stateAuthenticator?.status === "available"
|
|
263
366
|
? ReviewState.make({
|
|
264
|
-
version:
|
|
367
|
+
version: 1,
|
|
265
368
|
repository: metadata.repository,
|
|
266
369
|
pullRequestNumber: metadata.number,
|
|
267
370
|
baseRef: metadata.baseRef,
|
|
@@ -269,7 +372,7 @@ const settleReviewRun = (
|
|
|
269
372
|
headRef: metadata.headRef,
|
|
270
373
|
reviewedHeadSha: metadata.headSha,
|
|
271
374
|
profileFingerprint: executionContext.profileFingerprint,
|
|
272
|
-
|
|
375
|
+
settledScopeFingerprint: fingerprint,
|
|
273
376
|
reviewedPathCount: anchorFiles.length,
|
|
274
377
|
unresolvedFindings: activeFindings.map(toStoredFinding),
|
|
275
378
|
unresolvedConcerns: activeConcerns.map(toStoredConcern),
|
|
@@ -277,17 +380,19 @@ const settleReviewRun = (
|
|
|
277
380
|
unreviewedPasses: (core.unreviewedPasses ?? []).slice(0, MAX_STORED_UNREVIEWED_PASSES),
|
|
278
381
|
settled,
|
|
279
382
|
lastReviewMode: executionContext.mode,
|
|
383
|
+
...(adjudications.length === 0 ? {} : { adjudications }),
|
|
280
384
|
})
|
|
281
385
|
: undefined;
|
|
282
386
|
const continuity =
|
|
283
|
-
stateCandidate === undefined || executionContext
|
|
387
|
+
stateCandidate === undefined || executionContext.stateAuthenticator === undefined
|
|
284
388
|
? {
|
|
285
389
|
state: undefined,
|
|
286
390
|
marker: undefined,
|
|
287
|
-
notice:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
391
|
+
notice: !carriedScopeFits
|
|
392
|
+
? `carried unreviewed scope (${unreviewedPaths.length} paths) exceeded the ${MAX_STORED_UNREVIEWED_PATHS}-path continuity bound`
|
|
393
|
+
: !concernsHaveEvidencePaths
|
|
394
|
+
? "one or more review concerns lacked host-validated affected paths"
|
|
395
|
+
: executionContext.stateAuthenticator?.status === "unavailable"
|
|
291
396
|
? (executionContext.stateAuthenticator.unavailableReason ??
|
|
292
397
|
"authenticated continuity state is unavailable")
|
|
293
398
|
: undefined,
|
|
@@ -320,29 +425,29 @@ const settleReviewRun = (
|
|
|
320
425
|
unreviewedPaths,
|
|
321
426
|
carriedFindings,
|
|
322
427
|
carriedConcerns,
|
|
323
|
-
reviewMode: executionContext
|
|
324
|
-
reviewReason: executionContext
|
|
325
|
-
baselineSha: executionContext
|
|
428
|
+
reviewMode: executionContext.mode,
|
|
429
|
+
reviewReason: executionContext.reason,
|
|
430
|
+
baselineSha: executionContext.baselineSha,
|
|
326
431
|
reviewFilesVisible: files.length,
|
|
327
432
|
reviewTotalFiles,
|
|
328
433
|
stateMarker: continuity.marker,
|
|
329
434
|
stateNotice: continuity.notice,
|
|
435
|
+
...(adjudications.length === 0 ? {} : { adjudications }),
|
|
330
436
|
});
|
|
331
437
|
const shared = {
|
|
332
438
|
review,
|
|
333
439
|
activeFindings,
|
|
334
440
|
activeConcerns,
|
|
335
|
-
coverage: compatibilityCoverage(inputCoverage, assurance),
|
|
336
441
|
inputCoverage,
|
|
337
442
|
assurance,
|
|
338
443
|
unreviewedPaths,
|
|
339
444
|
plan,
|
|
340
445
|
turns: core.turns,
|
|
341
446
|
...(usage === undefined ? {} : { usage }),
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
: { reviewMode: executionContext.mode, reviewReason: executionContext.reason }),
|
|
447
|
+
reviewMode: executionContext.mode,
|
|
448
|
+
reviewReason: executionContext.reason,
|
|
345
449
|
...(continuity.state === undefined ? {} : { state: continuity.state }),
|
|
450
|
+
...(adjudications.length === 0 ? {} : { adjudications }),
|
|
346
451
|
};
|
|
347
452
|
if (!options.post) return ReviewRunOutcome.make(shared);
|
|
348
453
|
const publisher = yield* ReviewPublisher;
|
|
@@ -379,10 +484,12 @@ export const executeReview = <
|
|
|
379
484
|
const metadata = yield* source.metadata;
|
|
380
485
|
const files = yield* source.changedFiles;
|
|
381
486
|
const anchorFiles = yield* source.anchorFiles;
|
|
382
|
-
const executionContext =
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
487
|
+
const executionContext = yield* ReviewExecutionContext;
|
|
488
|
+
const continuity = yield* resolveReviewContinuityContext();
|
|
489
|
+
const mission = buildReviewMission(metadata, files, {
|
|
490
|
+
adjudicated: continuity.adjudications.map(renderAdjudicationContextLine),
|
|
491
|
+
priorFindings: continuity.priorFindingsOnScope.map(renderPriorFindingContextLine),
|
|
492
|
+
});
|
|
386
493
|
const fullMission = buildReviewMission(metadata, anchorFiles);
|
|
387
494
|
const fingerprint =
|
|
388
495
|
options.signature === undefined
|
|
@@ -402,7 +509,7 @@ export const executeReview = <
|
|
|
402
509
|
const review = yield* Schema.decodeUnknownEffect(CodeReview)(result.output);
|
|
403
510
|
const assessment = assessFlatReview({
|
|
404
511
|
files,
|
|
405
|
-
totalFiles: executionContext
|
|
512
|
+
totalFiles: executionContext.totalFiles,
|
|
406
513
|
anchorFiles,
|
|
407
514
|
totalAnchorFiles: metadata.totalChangedFiles,
|
|
408
515
|
events,
|
|
@@ -416,7 +523,7 @@ export const executeReview = <
|
|
|
416
523
|
unreviewedPaths: assessment.unreviewedPaths,
|
|
417
524
|
turns: result.turns,
|
|
418
525
|
},
|
|
419
|
-
{ metadata, files, anchorFiles, fingerprint, usage },
|
|
526
|
+
{ metadata, files, anchorFiles, fingerprint, usage, adjudications: continuity.adjudications },
|
|
420
527
|
options,
|
|
421
528
|
);
|
|
422
529
|
});
|
|
@@ -436,9 +543,7 @@ export const executeFanOutReview = <Provider, ModelProvides, ModelRequires>(
|
|
|
436
543
|
const metadata = yield* source.metadata;
|
|
437
544
|
const files = yield* source.changedFiles;
|
|
438
545
|
const anchorFiles = yield* source.anchorFiles;
|
|
439
|
-
const executionContext =
|
|
440
|
-
yield* Effect.serviceOption(ReviewExecutionContext),
|
|
441
|
-
);
|
|
546
|
+
const executionContext = yield* ReviewExecutionContext;
|
|
442
547
|
const fullMission = buildReviewMission(metadata, anchorFiles);
|
|
443
548
|
const fingerprint =
|
|
444
549
|
options.signature === undefined
|
|
@@ -446,14 +551,15 @@ export const executeFanOutReview = <Provider, ModelProvides, ModelRequires>(
|
|
|
446
551
|
: yield* computeChangesetFingerprint(anchorFiles, options.signature(fullMission));
|
|
447
552
|
|
|
448
553
|
const budget = yield* makeUsageBudget(options.limits ?? fanOutReviewBudgetLimits);
|
|
449
|
-
const totalFiles = executionContext
|
|
554
|
+
const totalFiles = executionContext.totalFiles;
|
|
555
|
+
const continuity = yield* resolveReviewContinuityContext();
|
|
450
556
|
const pipeline = yield* runFanOutReview(binding, {
|
|
451
557
|
files,
|
|
452
558
|
anchorFiles,
|
|
453
559
|
totalChangedFiles: totalFiles,
|
|
454
560
|
maxFindings: options.maxFindings,
|
|
455
561
|
budget: toRunBudgetHook(budget),
|
|
456
|
-
...(executionContext
|
|
562
|
+
...(executionContext.retryPaths.length > 0
|
|
457
563
|
? {
|
|
458
564
|
retry: {
|
|
459
565
|
paths: executionContext.retryPaths,
|
|
@@ -461,6 +567,14 @@ export const executeFanOutReview = <Provider, ModelProvides, ModelRequires>(
|
|
|
461
567
|
},
|
|
462
568
|
}
|
|
463
569
|
: {}),
|
|
570
|
+
...(continuity.adjudications.length > 0 || continuity.priorFindingsOnScope.length > 0
|
|
571
|
+
? {
|
|
572
|
+
priorContext: buildPriorReviewContext(
|
|
573
|
+
continuity.adjudications,
|
|
574
|
+
continuity.priorFindingsOnScope,
|
|
575
|
+
),
|
|
576
|
+
}
|
|
577
|
+
: {}),
|
|
464
578
|
});
|
|
465
579
|
const inputCoverage = fanOutInputCoverage({
|
|
466
580
|
plan: pipeline.plan,
|
|
@@ -486,7 +600,7 @@ export const executeFanOutReview = <Provider, ModelProvides, ModelRequires>(
|
|
|
486
600
|
),
|
|
487
601
|
turns: pipeline.turns,
|
|
488
602
|
},
|
|
489
|
-
{ metadata, files, anchorFiles, fingerprint, usage },
|
|
603
|
+
{ metadata, files, anchorFiles, fingerprint, usage, adjudications: continuity.adjudications },
|
|
490
604
|
options,
|
|
491
605
|
);
|
|
492
606
|
});
|