@effect-agent/pr-review 0.1.0-beta.22 → 0.1.0-beta.24
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 +72 -48
- package/dist/action.d.mts +13 -13
- package/dist/action.mjs +42 -25
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{fan-out-n-00ppWr.d.mts → fan-out-Bi1v0VaU.d.mts} +314 -307
- package/dist/{github-BgtP7Rdv.mjs → github-C6jrBLA2.mjs} +763 -285
- package/dist/github-C6jrBLA2.mjs.map +1 -0
- package/dist/index.d.mts +84 -126
- package/dist/index.mjs +11 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{providers-BguZK4B_.mjs → providers-2Jao2ZAX.mjs} +173 -585
- package/dist/providers-2Jao2ZAX.mjs.map +1 -0
- package/dist/testing.d.mts +6 -15
- package/dist/testing.mjs +14 -42
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +58 -34
- package/src/internal/action-entry.ts +0 -1
- package/src/internal/coverage.ts +147 -548
- package/src/internal/factory.ts +33 -54
- package/src/internal/fan-out-scripted.ts +26 -80
- package/src/internal/fan-out.ts +669 -432
- package/src/internal/fingerprint.ts +16 -10
- package/src/internal/fixtures.ts +6 -0
- package/src/internal/github.ts +36 -7
- package/src/internal/profiles.ts +8 -8
- package/src/internal/render.ts +34 -45
- package/src/internal/retirement.ts +3 -1
- package/src/internal/review-state.ts +204 -89
- package/src/internal/review-units.ts +25 -0
- package/src/internal/run.ts +231 -174
- package/src/internal/source.ts +1 -1
- package/dist/github-BgtP7Rdv.mjs.map +0 -1
- package/dist/providers-BguZK4B_.mjs.map +0 -1
package/src/action.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
ReviewExecutionContext,
|
|
25
25
|
ReviewHeadComparison,
|
|
26
26
|
ReviewStateAuthenticator,
|
|
27
|
+
isLineageAncestor,
|
|
27
28
|
type ReviewMode,
|
|
28
29
|
type ReviewState,
|
|
29
30
|
selectReviewRange,
|
|
@@ -51,10 +52,6 @@ import {
|
|
|
51
52
|
// nothing.
|
|
52
53
|
// ---------------------------------------------------------------------------
|
|
53
54
|
|
|
54
|
-
/** Deprecated compatibility input; host-derived conclusions are unconditional. */
|
|
55
|
-
export const FailOnPolicy = Schema.Literals(["never", "request-changes"]);
|
|
56
|
-
export type FailOnPolicy = typeof FailOnPolicy.Type;
|
|
57
|
-
|
|
58
55
|
export const ReviewCheckConclusion = Schema.Literals(["success", "blocking", "incomplete"]);
|
|
59
56
|
export type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;
|
|
60
57
|
|
|
@@ -96,8 +93,6 @@ export interface ResolvedActionInputs {
|
|
|
96
93
|
readonly maxFindings: number | undefined;
|
|
97
94
|
readonly maxDurationMinutes: number | undefined;
|
|
98
95
|
readonly reviewMode: ReviewMode;
|
|
99
|
-
/** Deprecated compatibility input; conclusions are always conservative. */
|
|
100
|
-
readonly failOn: FailOnPolicy;
|
|
101
96
|
readonly skipUnchanged: boolean;
|
|
102
97
|
readonly retireStaleReviews: boolean;
|
|
103
98
|
readonly progressComment: boolean;
|
|
@@ -135,9 +130,6 @@ export const resolveActionInputs = Effect.fn("resolveActionInputs")(function* ()
|
|
|
135
130
|
const reviewMode = yield* Config.literals(["incremental", "final"], "PR_REVIEW_MODE").pipe(
|
|
136
131
|
Config.withDefault<ReviewMode>("incremental"),
|
|
137
132
|
);
|
|
138
|
-
const failOn = yield* Config.literals(["never", "request-changes"], "PR_REVIEW_FAIL_ON").pipe(
|
|
139
|
-
Config.withDefault<FailOnPolicy>("never"),
|
|
140
|
-
);
|
|
141
133
|
const skipUnchanged = yield* Config.boolean("PR_REVIEW_SKIP_UNCHANGED").pipe(
|
|
142
134
|
Config.withDefault(true),
|
|
143
135
|
);
|
|
@@ -163,7 +155,6 @@ export const resolveActionInputs = Effect.fn("resolveActionInputs")(function* ()
|
|
|
163
155
|
maxFindings: Option.getOrUndefined(maxFindings),
|
|
164
156
|
maxDurationMinutes,
|
|
165
157
|
reviewMode,
|
|
166
|
-
failOn,
|
|
167
158
|
skipUnchanged,
|
|
168
159
|
retireStaleReviews,
|
|
169
160
|
progressComment,
|
|
@@ -276,6 +267,7 @@ const outcomeOutputs = (
|
|
|
276
267
|
["inline-comments", String(outcome.plan.comments.length)],
|
|
277
268
|
["demoted-findings", String(outcome.plan.demoted.length)],
|
|
278
269
|
["concerns", String(outcome.review.concerns?.length ?? 0)],
|
|
270
|
+
["unreviewed-paths", String(outcome.unreviewedPaths.length)],
|
|
279
271
|
...(outcome.usage === undefined
|
|
280
272
|
? []
|
|
281
273
|
: ([
|
|
@@ -296,14 +288,15 @@ const outcomeSummary = (
|
|
|
296
288
|
`- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? "full"}`,
|
|
297
289
|
`- Review assurance: **${outcome.assurance.status}** · general discovery ${outcome.assurance.completedGeneralDiscoveryPasses}/${outcome.assurance.requiredGeneralDiscoveryPasses} · specialist ${outcome.assurance.completedSpecialistPasses}/${outcome.assurance.requiredSpecialistPasses} · verification ${outcome.assurance.completedVerificationPasses}/${outcome.assurance.requiredVerificationPasses}`,
|
|
298
290
|
`- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,
|
|
299
|
-
...(
|
|
300
|
-
...(outcome.usage === undefined
|
|
291
|
+
...(outcome.unreviewedPaths.length === 0
|
|
301
292
|
? []
|
|
302
293
|
: [
|
|
303
|
-
`-
|
|
304
|
-
outcome.usageScope === "coordinator" ? " (coordinator)" : ""
|
|
305
|
-
}`,
|
|
294
|
+
`- Carried forward: ${outcome.unreviewedPaths.length} unreviewed path(s) retried automatically on the next run (reviewer-side gap, not a code defect)`,
|
|
306
295
|
]),
|
|
296
|
+
...(modelLabel === undefined ? [] : [`- Model: \`${modelLabel}\``]),
|
|
297
|
+
...(outcome.usage === undefined
|
|
298
|
+
? []
|
|
299
|
+
: [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out`]),
|
|
307
300
|
...(outcome.published === undefined
|
|
308
301
|
? ["- Dry run: nothing posted"]
|
|
309
302
|
: [`- Posted: ${outcome.published.url}`]),
|
|
@@ -376,32 +369,44 @@ const blockingReasons = (input: {
|
|
|
376
369
|
.map((concern) => `blocking concern: ${concern.title}`),
|
|
377
370
|
];
|
|
378
371
|
|
|
379
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Host-derived check conclusion; model verdict prose cannot weaken it.
|
|
374
|
+
*
|
|
375
|
+
* Blocking code findings outrank machinery gaps — they are the actionable
|
|
376
|
+
* signal. A machinery gap (incomplete input, unsettled passes) concludes
|
|
377
|
+
* `incomplete` with reasons that explicitly say the failure is reviewer-side
|
|
378
|
+
* uncertainty carried forward for retry, never an invitation to change code.
|
|
379
|
+
* The flat reviewer's constant `unverified` assurance is not a gap.
|
|
380
|
+
*/
|
|
380
381
|
export const concludeReviewOutcome = (
|
|
381
382
|
outcome: ReviewRunOutcome,
|
|
382
383
|
): {
|
|
383
384
|
readonly conclusion: ReviewCheckConclusion;
|
|
384
385
|
readonly reasons: ReadonlyArray<string>;
|
|
385
386
|
} => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
387
|
+
const machinery: Array<string> = [];
|
|
388
|
+
if (outcome.inputCoverage.status === "incomplete" || outcome.assurance.status === "incomplete") {
|
|
389
|
+
machinery.push(
|
|
390
|
+
outcome.unreviewedPaths.length > 0
|
|
391
|
+
? `review infrastructure did not settle — a reviewer-side gap, not a code defect; ${outcome.unreviewedPaths.length} path(s) are carried forward and retried automatically on the next run`
|
|
392
|
+
: "review infrastructure did not settle — a reviewer-side gap, not a code defect",
|
|
393
|
+
);
|
|
394
|
+
if (outcome.inputCoverage.status === "incomplete") {
|
|
395
|
+
machinery.push(...outcome.inputCoverage.reasons);
|
|
396
|
+
}
|
|
397
|
+
if (outcome.assurance.status === "incomplete") {
|
|
398
|
+
machinery.push(...outcome.assurance.reasons);
|
|
399
|
+
}
|
|
397
400
|
}
|
|
398
|
-
const
|
|
401
|
+
const blocking = blockingReasons({
|
|
399
402
|
findings: outcome.activeFindings,
|
|
400
403
|
concerns: outcome.activeConcerns,
|
|
401
404
|
});
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
+
if (blocking.length > 0) {
|
|
406
|
+
return { conclusion: "blocking", reasons: [...blocking, ...machinery] };
|
|
407
|
+
}
|
|
408
|
+
if (machinery.length > 0) return { conclusion: "incomplete", reasons: machinery };
|
|
409
|
+
return { conclusion: "success", reasons: [] };
|
|
405
410
|
};
|
|
406
411
|
|
|
407
412
|
const concludeReviewState = (state: ReviewState) => {
|
|
@@ -462,7 +467,6 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
462
467
|
reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>,
|
|
463
468
|
options: {
|
|
464
469
|
readonly post?: boolean | undefined;
|
|
465
|
-
readonly failOn?: FailOnPolicy | undefined;
|
|
466
470
|
/** Skip model execution when the current head already has complete stored coverage. */
|
|
467
471
|
readonly skipUnchanged?: boolean | undefined;
|
|
468
472
|
/** Incremental by default; `final` deliberately re-reviews the full PR diff. */
|
|
@@ -533,6 +537,9 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
533
537
|
(options.reviewMode ?? "incremental") === "incremental" &&
|
|
534
538
|
options.skipUnchanged !== false &&
|
|
535
539
|
recovered.state !== undefined &&
|
|
540
|
+
// Only a fully settled run may be skipped over: an unsettled state
|
|
541
|
+
// carries retryable scope the next run must actually retry.
|
|
542
|
+
recovered.state.settled &&
|
|
536
543
|
currentFingerprint !== undefined &&
|
|
537
544
|
validateReviewState(recovered.state, metadata, profileFingerprint) === undefined &&
|
|
538
545
|
recovered.state.acceptedScopeFingerprint === currentFingerprint
|
|
@@ -552,6 +559,7 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
552
559
|
}
|
|
553
560
|
let comparison: ReviewHeadComparison | undefined;
|
|
554
561
|
let baseComparison: ReviewHeadComparison | undefined;
|
|
562
|
+
let contentComparison: ReviewHeadComparison | undefined;
|
|
555
563
|
if (
|
|
556
564
|
(options.reviewMode ?? "incremental") === "incremental" &&
|
|
557
565
|
recovered.state !== undefined
|
|
@@ -587,6 +595,21 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
587
595
|
});
|
|
588
596
|
}
|
|
589
597
|
}
|
|
598
|
+
if (
|
|
599
|
+
recovered.state.reviewedHeadSha !== metadata.headSha &&
|
|
600
|
+
(comparison === undefined ||
|
|
601
|
+
!isLineageAncestor(comparison, recovered.state, metadata.headSha))
|
|
602
|
+
) {
|
|
603
|
+
contentComparison = yield* history
|
|
604
|
+
.compareTrees(recovered.state.reviewedHeadSha, metadata.headSha)
|
|
605
|
+
.pipe(Effect.orElseSucceed(() => undefined));
|
|
606
|
+
if (contentComparison !== undefined && reviewer.filterFiles !== undefined) {
|
|
607
|
+
contentComparison = ReviewHeadComparison.make({
|
|
608
|
+
...contentComparison,
|
|
609
|
+
files: reviewer.filterFiles(contentComparison.files),
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
590
613
|
}
|
|
591
614
|
selection = {
|
|
592
615
|
...selectReviewRange({
|
|
@@ -597,6 +620,7 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
597
620
|
priorState: recovered.state,
|
|
598
621
|
comparison,
|
|
599
622
|
baseComparison,
|
|
623
|
+
contentComparison,
|
|
600
624
|
lookupFailure: recovered.failure,
|
|
601
625
|
}),
|
|
602
626
|
stateAuthenticator,
|
|
@@ -605,7 +629,8 @@ export const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never
|
|
|
605
629
|
options.skipUnchanged !== false &&
|
|
606
630
|
selection.mode === "incremental" &&
|
|
607
631
|
selection.files.length === 0 &&
|
|
608
|
-
selection.priorState !== undefined
|
|
632
|
+
selection.priorState !== undefined &&
|
|
633
|
+
selection.priorState.settled
|
|
609
634
|
) {
|
|
610
635
|
const reason = "no changed review scope since the last settled review head";
|
|
611
636
|
return yield* skipCoveredReview({
|
|
@@ -747,7 +772,6 @@ export const reviewActionProgram = Effect.gen(function* () {
|
|
|
747
772
|
};
|
|
748
773
|
const harness = {
|
|
749
774
|
post: inputs.post,
|
|
750
|
-
failOn: inputs.failOn,
|
|
751
775
|
skipUnchanged: inputs.skipUnchanged,
|
|
752
776
|
reviewMode: inputs.reviewMode,
|
|
753
777
|
retireStaleReviews: inputs.retireStaleReviews,
|
|
@@ -23,7 +23,6 @@ const INPUT_TO_ENV: ReadonlyArray<readonly [input: string, env: string]> = [
|
|
|
23
23
|
["INPUT_IGNORE", "PR_REVIEW_IGNORE"],
|
|
24
24
|
["INPUT_MAX-FINDINGS", "PR_REVIEW_MAX_FINDINGS"],
|
|
25
25
|
["INPUT_REVIEW-MODE", "PR_REVIEW_MODE"],
|
|
26
|
-
["INPUT_FAIL-ON", "PR_REVIEW_FAIL_ON"],
|
|
27
26
|
["INPUT_SKIP-UNCHANGED", "PR_REVIEW_SKIP_UNCHANGED"],
|
|
28
27
|
["INPUT_RETIRE-STALE-REVIEWS", "PR_REVIEW_RETIRE_STALE_REVIEWS"],
|
|
29
28
|
["INPUT_PROGRESS-COMMENT", "PR_REVIEW_PROGRESS_COMMENT"],
|