@effect-agent/pr-review 0.1.0-beta.21 → 0.1.0-beta.23
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 +63 -46
- package/dist/action.d.mts +13 -13
- package/dist/action.mjs +33 -25
- package/dist/action.mjs.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{fan-out-D5xrmadQ.d.mts → fan-out-BJBTAYuh.d.mts} +278 -303
- package/dist/{github-DSqZp3Ce.mjs → github-BbwYzNrC.mjs} +588 -243
- package/dist/github-BbwYzNrC.mjs.map +1 -0
- package/dist/index.d.mts +72 -116
- package/dist/index.mjs +11 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{providers-CblG1G9b.mjs → providers-NyP-4rS6.mjs} +162 -578
- package/dist/providers-NyP-4rS6.mjs.map +1 -0
- package/dist/testing.d.mts +4 -15
- package/dist/testing.mjs +12 -41
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -2
- package/src/action.ts +40 -34
- package/src/internal/action-entry.ts +0 -1
- package/src/internal/coverage.ts +147 -532
- package/src/internal/factory.ts +29 -50
- package/src/internal/fan-out-scripted.ts +26 -80
- package/src/internal/fan-out.ts +577 -426
- 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-agent.ts +6 -1
- package/src/internal/review-state.ts +43 -19
- package/src/internal/review-units.ts +25 -0
- package/src/internal/run.ts +211 -174
- package/src/internal/source.ts +1 -1
- package/dist/github-DSqZp3Ce.mjs.map +0 -1
- package/dist/providers-CblG1G9b.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -59,21 +59,26 @@ re-validated against the parsed unified diff (invalid ones are demoted into
|
|
|
59
59
|
the review body with the reason named, never trusted), the findings bound is
|
|
60
60
|
enforced host-side, and publication happens only through the
|
|
61
61
|
`ReviewPublisher` port after the run settles. `PrReview.makeFanOut` builds the
|
|
62
|
-
assured
|
|
62
|
+
assured host-scheduled variant. The host deterministically partitions bounded
|
|
63
63
|
evidence, classifies high-risk units, requires a general discovery pass for
|
|
64
64
|
every unit, adds a fresh specialist discovery pass for every unit, and uses
|
|
65
65
|
deterministic host-classified risk categories as explicit focus labels. A
|
|
66
|
-
fresh verifier must confirm or reject every discovered candidate.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
fresh verifier must confirm or reject every discovered candidate. Every pass
|
|
67
|
+
runs as a bounded evidence-only child dispatched directly by host code with
|
|
68
|
+
Effect structured concurrency — there is no coordinator model and no
|
|
69
|
+
delegation tool, so nothing depends on a model copying the plan correctly. A
|
|
70
|
+
pass that fails (child fault, malformed or misdirected output) is retried
|
|
71
|
+
once; a pass that stays failed is reported and its unit's paths carry forward
|
|
72
|
+
as retryable scope. Verifiers receive the exact candidate claims and the
|
|
73
|
+
complete bounded unit, including neighboring evidence that can falsify a
|
|
74
|
+
locally plausible claim; they do not receive discovery reasoning.
|
|
75
|
+
The host builds publishable findings and concerns from exact confirmed
|
|
76
|
+
candidate IDs and deduplicates byte-identical cross-pass claims in
|
|
77
|
+
deterministic plan order; a discovery claim anchored outside its assigned
|
|
78
|
+
evidence is discarded and counted, never published and never pass-fatal. The
|
|
79
|
+
summary and verdict are composed deterministically from the confirmed
|
|
80
|
+
severities. Shared `guidance` reaches both discovery and verification;
|
|
81
|
+
`maxFindings` remains a host-enforced publication cap.
|
|
77
82
|
|
|
78
83
|
GitHub may omit the `patch` field for large textual files as well as binary
|
|
79
84
|
files. The GitHub source recovers a missing patch by reading bounded, strict
|
|
@@ -81,24 +86,29 @@ UTF-8 base/head content: additions require the head, deletions require the
|
|
|
81
86
|
base, and other changes require both. Reviewers receive that content through
|
|
82
87
|
the ordinary diff-read tool with non-anchorable `B`/`H` line labels and must
|
|
83
88
|
report defects as review-body concerns. Invalid UTF-8, binary NUL content,
|
|
84
|
-
missing sides, files beyond the per-side read bound
|
|
85
|
-
|
|
89
|
+
missing sides, and files beyond the per-side read bound leave a path with no
|
|
90
|
+
reviewable textual evidence. Such paths keep input coverage incomplete and are
|
|
91
|
+
carried for as long as they are part of the pull request — an unreviewable
|
|
92
|
+
change must never authorize a green check; exclude them deliberately with
|
|
93
|
+
ignore globs when that is intended.
|
|
86
94
|
|
|
87
95
|
## Assurance model
|
|
88
96
|
|
|
89
97
|
The public result deliberately separates two claims:
|
|
90
98
|
|
|
91
|
-
- **Input coverage** says every required path in the selected
|
|
92
|
-
assigned every deterministic bounded evidence shard and
|
|
93
|
-
partially assigned and
|
|
94
|
-
with a bounded identifier sample, undiffable paths,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
- **Review assurance** says every
|
|
99
|
-
independent specialist pass, and candidate-verification
|
|
100
|
-
|
|
101
|
-
candidate counts plus failed pass IDs.
|
|
99
|
+
- **Input coverage** says every reviewable required path in the selected
|
|
100
|
+
scope was assigned every deterministic bounded evidence shard and
|
|
101
|
+
separately names partially assigned and over-capacity paths, the exact
|
|
102
|
+
unassigned-shard count with a bounded identifier sample, undiffable paths,
|
|
103
|
+
and source truncation. A large textual diff is split across shards and
|
|
104
|
+
units instead of being called partial merely for exceeding one prompt
|
|
105
|
+
chunk. Input coverage does not say the model understood the evidence.
|
|
106
|
+
- **Review assurance** says every scheduled general discovery pass, required
|
|
107
|
+
independent specialist pass, and candidate-verification pass settled after
|
|
108
|
+
at most one retry. It reports discovered, confirmed, rejected, unsettled,
|
|
109
|
+
and discarded (invalid-anchor) candidate counts plus failed pass IDs. A
|
|
110
|
+
failed pass is a reviewer-side gap: its unit's paths carry forward as
|
|
111
|
+
retryable scope and the next incremental run re-reviews exactly them.
|
|
102
112
|
|
|
103
113
|
`settled` assurance means the configured work completed; it never means the
|
|
104
114
|
review is exhaustive or the pull request is defect-free. Risk classification
|
|
@@ -126,10 +136,9 @@ discovery/specialist/verification settlement, and candidate dispositions.
|
|
|
126
136
|
Below the summary, a collapsed **📝 Walkthrough** table carries the
|
|
127
137
|
model's one-sentence per-file change summaries — walkthrough paths are
|
|
128
138
|
validated like finding anchors, so entries naming files outside the changeset
|
|
129
|
-
are dropped. Under fan-out
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
nor the coordinator can smuggle or invent entries. Non-anchored `concerns`
|
|
139
|
+
are dropped. Under fan-out only summaries a
|
|
140
|
+
successfully settled general pass actually reported for its own unit's paths
|
|
141
|
+
survive, so a child cannot smuggle or invent entries. Non-anchored `concerns`
|
|
133
142
|
(deletion plans, rollout sequencing,
|
|
134
143
|
coverage gaps, scope questions — things with no diff line to point at) render
|
|
135
144
|
as severity-tagged sections; demoted findings and findings carried from
|
|
@@ -137,7 +146,8 @@ unchanged scope collapse into counted `<details>` sections.
|
|
|
137
146
|
|
|
138
147
|
Each inline comment is headed by its severity plus an optional model-claimed
|
|
139
148
|
category chip (`**[⚠️ important · security] …**`), carries a committable
|
|
140
|
-
GitHub `suggestion` fence when
|
|
149
|
+
GitHub `suggestion` fence when independent verification settled the model's
|
|
150
|
+
suggestion as committable replacement source, and ends with a
|
|
141
151
|
collapsed **🤖 Prompt for AI agents** — a copy-paste instruction derived
|
|
142
152
|
host-side from the validated finding, opening with a fixed preamble telling
|
|
143
153
|
the receiving agent to treat the finding content as untrusted review data.
|
|
@@ -171,17 +181,23 @@ ordinary gate.
|
|
|
171
181
|
|
|
172
182
|
## Incremental Action reviews
|
|
173
183
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
Every completed Action review that can be signed carries bounded, versioned,
|
|
185
|
+
HMAC-authenticated continuity state: the exact PR/base/head lineage, profile
|
|
186
|
+
and settled-scope fingerprints, the still-unresolved findings and concerns,
|
|
187
|
+
any retryable unreviewed paths, and whether the run fully settled. The
|
|
188
|
+
baseline is monotone by design — a flaky or failed pass carries exactly its
|
|
189
|
+
own scope forward instead of freezing the baseline and reopening everything
|
|
190
|
+
reviewed since (the non-converging loop of issue #131). A later Action run
|
|
191
|
+
validates the state and reviews the GitHub comparison from that reviewed head
|
|
192
|
+
to the current head plus the carried unreviewed paths, not the complete
|
|
193
|
+
base...HEAD diff. Unchanged settled scope is not sent back to the model;
|
|
194
|
+
unchanged unresolved findings remain active; changed or reverted paths
|
|
195
|
+
invalidate their prior findings and receive fresh discovery and verification.
|
|
196
|
+
Whole overflow files are reviewed in bounded installments across pushes
|
|
197
|
+
through the same carry; a single file beyond total plan capacity and
|
|
198
|
+
undiffable paths stay explicitly incomplete instead — an unreviewable tail
|
|
199
|
+
never moves behind a green check. Non-anchored concerns are carried conservatively
|
|
200
|
+
until a full audit because they cannot be mapped safely to one path.
|
|
185
201
|
|
|
186
202
|
The state marker must be terminal, signed with the configured stable
|
|
187
203
|
`PR_REVIEW_STATE_SECRET`, authored by the configured review-posting bot, and
|
|
@@ -197,11 +213,12 @@ full unless the authenticated settled-scope fingerprint still matches. The
|
|
|
197
213
|
schema field retains the compatibility name `acceptedScopeFingerprint`. That
|
|
198
214
|
fingerprint hashes the ignore-filtered effective diff, patchless base/head
|
|
199
215
|
evidence, PR framing, and review-shaping profile while excluding commit IDs,
|
|
200
|
-
base ancestry, and unified-diff hunk coordinates. Re-running the same
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
review
|
|
216
|
+
base ancestry, and unified-diff hunk coordinates. Re-running the same head or a
|
|
217
|
+
patch-equivalent rebase skips model execution by default ONLY when the stored
|
|
218
|
+
state fully settled — a state carrying unreviewed scope is always retried.
|
|
219
|
+
The preserved skip keeps its stored blocking/success conclusion and posts no
|
|
220
|
+
duplicate review comments. Missing/corrupt state and any fingerprint or
|
|
221
|
+
profile mismatch review conservatively.
|
|
205
222
|
|
|
206
223
|
After a new state-bearing Action review posts, prior marker-bearing bot reviews
|
|
207
224
|
are retired by default: their bodies become collapsed, superseded history,
|
package/dist/action.d.mts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { L as PriorReviews, P as GitHubApiFailure, X as ReviewRetirementHost, ar as PullRequestSource, bt as ReviewStateAuthenticator, ht as ReviewMode, ir as PullRequestMetadata, lr as ChangedFile, or as PullRequestSourceFailure, z as ReviewPublisher } from "./fan-out-BJBTAYuh.mjs";
|
|
2
2
|
import { EffortPosition, InvalidEffortInput, ReviewProgressReporter, ReviewProvider, ReviewRunOutcome, ReviewTargetUnresolved, RunReviewOptions } from "./index.mjs";
|
|
3
3
|
import { Config, Effect, FileSystem, Schema } from "effect";
|
|
4
4
|
import { BudgetExceeded } from "effect-agent";
|
|
5
5
|
//#region src/action.d.ts
|
|
6
|
-
/** Deprecated compatibility input; host-derived conclusions are unconditional. */
|
|
7
|
-
declare const FailOnPolicy: Schema.Literals<readonly ["never", "request-changes"]>;
|
|
8
|
-
type FailOnPolicy = typeof FailOnPolicy.Type;
|
|
9
6
|
declare const ReviewCheckConclusion: Schema.Literals<readonly ["success", "blocking", "incomplete"]>;
|
|
10
7
|
type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;
|
|
11
8
|
declare const ReviewGateFailed_base: Schema.Class<ReviewGateFailed, Schema.TaggedStruct<"ReviewGateFailed", {
|
|
@@ -37,8 +34,6 @@ interface ResolvedActionInputs {
|
|
|
37
34
|
readonly maxFindings: number | undefined;
|
|
38
35
|
readonly maxDurationMinutes: number | undefined;
|
|
39
36
|
readonly reviewMode: ReviewMode;
|
|
40
|
-
/** Deprecated compatibility input; conclusions are always conservative. */
|
|
41
|
-
readonly failOn: FailOnPolicy;
|
|
42
37
|
readonly skipUnchanged: boolean;
|
|
43
38
|
readonly retireStaleReviews: boolean;
|
|
44
39
|
readonly progressComment: boolean;
|
|
@@ -57,7 +52,6 @@ declare const resolveActionInputs: () => Effect.Effect<{
|
|
|
57
52
|
maxFindings: number | undefined;
|
|
58
53
|
maxDurationMinutes: number | undefined;
|
|
59
54
|
reviewMode: "final" | "incremental";
|
|
60
|
-
failOn: "never" | "request-changes";
|
|
61
55
|
skipUnchanged: boolean;
|
|
62
56
|
retireStaleReviews: boolean;
|
|
63
57
|
progressComment: boolean;
|
|
@@ -113,7 +107,15 @@ interface HarnessedReviewer<E, R, FingerprintE, FingerprintR> {
|
|
|
113
107
|
}, FingerprintE, FingerprintR> | undefined;
|
|
114
108
|
readonly filterFiles?: ((files: ReadonlyArray<ChangedFile>) => ReadonlyArray<ChangedFile>) | undefined;
|
|
115
109
|
}
|
|
116
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Host-derived check conclusion; model verdict prose cannot weaken it.
|
|
112
|
+
*
|
|
113
|
+
* Blocking code findings outrank machinery gaps — they are the actionable
|
|
114
|
+
* signal. A machinery gap (incomplete input, unsettled passes) concludes
|
|
115
|
+
* `incomplete` with reasons that explicitly say the failure is reviewer-side
|
|
116
|
+
* uncertainty carried forward for retry, never an invitation to change code.
|
|
117
|
+
* The flat reviewer's constant `unverified` assurance is not a gap.
|
|
118
|
+
*/
|
|
117
119
|
declare const concludeReviewOutcome: (outcome: ReviewRunOutcome) => {
|
|
118
120
|
readonly conclusion: ReviewCheckConclusion;
|
|
119
121
|
readonly reasons: ReadonlyArray<string>;
|
|
@@ -129,7 +131,6 @@ declare const concludeReviewOutcome: (outcome: ReviewRunOutcome) => {
|
|
|
129
131
|
*/
|
|
130
132
|
declare const runReviewAction: <E, R, FingerprintE = never, FingerprintR = never>(reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>, options?: {
|
|
131
133
|
readonly post?: boolean | undefined;
|
|
132
|
-
readonly failOn?: FailOnPolicy | undefined;
|
|
133
134
|
/** Skip model execution when the current head already has complete stored coverage. */
|
|
134
135
|
readonly skipUnchanged?: boolean | undefined;
|
|
135
136
|
/** Incremental by default; `final` deliberately re-reviews the full PR diff. */
|
|
@@ -165,7 +166,7 @@ declare const reviewActionProgram: Effect.Effect<{
|
|
|
165
166
|
} | {
|
|
166
167
|
_tag: "Completed";
|
|
167
168
|
outcome: ReviewRunOutcome;
|
|
168
|
-
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
169
|
+
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
169
170
|
/** Run the packaged action program on Node; the bundled action entrypoint. */
|
|
170
171
|
declare const main: () => void;
|
|
171
172
|
/** The packaged GitHub Actions surface. */
|
|
@@ -183,7 +184,6 @@ declare const PrReviewAction: {
|
|
|
183
184
|
maxFindings: number | undefined;
|
|
184
185
|
maxDurationMinutes: number | undefined;
|
|
185
186
|
reviewMode: "final" | "incremental";
|
|
186
|
-
failOn: "never" | "request-changes";
|
|
187
187
|
skipUnchanged: boolean;
|
|
188
188
|
retireStaleReviews: boolean;
|
|
189
189
|
progressComment: boolean;
|
|
@@ -195,9 +195,9 @@ declare const PrReviewAction: {
|
|
|
195
195
|
} | {
|
|
196
196
|
_tag: "Completed";
|
|
197
197
|
outcome: ReviewRunOutcome;
|
|
198
|
-
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
198
|
+
}, import("effect-agent").AgentApprovalDenied | import("effect-agent").AgentApprovalPending | import("effect-agent").AgentChildPending | import("effect-agent").AgentInputError | import("effect-agent").AgentOutputError | import("effect-agent").AgentPolicyError | import("effect-agent").AgentToolAuthorizationDenied | import("effect/unstable/ai/AiError").AiError | import("effect-agent").BudgetAdapterError | BudgetExceeded | Config.ConfigError | import("effect-agent").ContextOverflowError | GitHubApiFailure | GuidanceFileUnreadable | InvalidEffortInput | InvalidMaxDurationInput | import("effect-agent").ModelProtocolError | import("effect/PlatformError").PlatformError | PullRequestSourceFailure | ReviewGateFailed | ReviewTargetUnresolved | Schema.SchemaError, FileSystem.FileSystem | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
199
199
|
readonly main: typeof main;
|
|
200
200
|
};
|
|
201
201
|
//#endregion
|
|
202
|
-
export {
|
|
202
|
+
export { GuidanceFileUnreadable, HarnessedReviewer, InvalidMaxDurationInput, MAX_GUIDANCE_FILE_CHARS, PrReviewAction, ResolvedActionInputs, ReviewActionResult, ReviewCheckConclusion, ReviewGateFailed, concludeReviewOutcome, main, resolveActionInputs, resolveGuidance, reviewActionProgram, runReviewAction, writeActionOutputs, writeStepSummary };
|
|
203
203
|
//# sourceMappingURL=action.d.mts.map
|
package/dist/action.mjs
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { B as selectReviewRange, E as ReviewHeadComparison, G as validateReviewState, K as webCryptoReviewStateAuthenticatorLayer, Kn as normalizeRepoRelativePath, T as ReviewExecutionContext, Un as PullRequestSource, V as selectedPullRequestSourceLayer, W as unavailableReviewStateAuthenticatorLayer, a as PriorReviews, j as ReviewStateAuthenticator, x as retireStaleReviews } from "./github-BbwYzNrC.mjs";
|
|
2
|
+
import { A as reviewBudgetLimits, C as PrReview, H as InvalidEffortInput, W as parseEffortPosition, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, g as ReviewProgressReporter, k as fanOutReviewBudgetLimits, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, p as readGitHubEvent, s as makeAnthropicReviewModel } from "./providers-NyP-4rS6.mjs";
|
|
3
3
|
import { n as compactReviewLoggingLayer } from "./logging-Q4j0oub-.mjs";
|
|
4
4
|
import { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from "effect";
|
|
5
5
|
import { BudgetExceeded, UsageBudgetLimits } from "effect-agent";
|
|
6
6
|
import { FetchHttpClient } from "effect/unstable/http";
|
|
7
7
|
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
8
8
|
//#region src/action.ts
|
|
9
|
-
/** Deprecated compatibility input; host-derived conclusions are unconditional. */
|
|
10
|
-
const FailOnPolicy = Schema.Literals(["never", "request-changes"]);
|
|
11
9
|
const ReviewCheckConclusion = Schema.Literals([
|
|
12
10
|
"success",
|
|
13
11
|
"blocking",
|
|
@@ -48,7 +46,6 @@ const resolveActionInputs = Effect.fn("resolveActionInputs")(function* () {
|
|
|
48
46
|
const ignoreRaw = yield* Config.string("PR_REVIEW_IGNORE").pipe(Config.withDefault(""));
|
|
49
47
|
const maxFindings = yield* Config.option(Config.int("PR_REVIEW_MAX_FINDINGS"));
|
|
50
48
|
const reviewMode = yield* Config.literals(["incremental", "final"], "PR_REVIEW_MODE").pipe(Config.withDefault("incremental"));
|
|
51
|
-
const failOn = yield* Config.literals(["never", "request-changes"], "PR_REVIEW_FAIL_ON").pipe(Config.withDefault("never"));
|
|
52
49
|
const skipUnchanged = yield* Config.boolean("PR_REVIEW_SKIP_UNCHANGED").pipe(Config.withDefault(true));
|
|
53
50
|
const retireStaleReviews = yield* Config.boolean("PR_REVIEW_RETIRE_STALE_REVIEWS").pipe(Config.withDefault(true));
|
|
54
51
|
const progressComment = yield* Config.boolean("PR_REVIEW_PROGRESS_COMMENT").pipe(Config.withDefault(true));
|
|
@@ -65,7 +62,6 @@ const resolveActionInputs = Effect.fn("resolveActionInputs")(function* () {
|
|
|
65
62
|
maxFindings: Option.getOrUndefined(maxFindings),
|
|
66
63
|
maxDurationMinutes,
|
|
67
64
|
reviewMode,
|
|
68
|
-
failOn,
|
|
69
65
|
skipUnchanged,
|
|
70
66
|
retireStaleReviews,
|
|
71
67
|
progressComment
|
|
@@ -137,6 +133,7 @@ const outcomeOutputs = (outcome, conclusion) => [
|
|
|
137
133
|
["inline-comments", String(outcome.plan.comments.length)],
|
|
138
134
|
["demoted-findings", String(outcome.plan.demoted.length)],
|
|
139
135
|
["concerns", String(outcome.review.concerns?.length ?? 0)],
|
|
136
|
+
["unreviewed-paths", String(outcome.unreviewedPaths.length)],
|
|
140
137
|
...outcome.usage === void 0 ? [] : [["input-tokens", String(outcome.usage.inputTokens)], ["output-tokens", String(outcome.usage.outputTokens)]],
|
|
141
138
|
["review-url", outcome.published?.url ?? ""]
|
|
142
139
|
];
|
|
@@ -147,8 +144,9 @@ const outcomeSummary = (outcome, modelLabel, conclusion) => [
|
|
|
147
144
|
`- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? "full"}`,
|
|
148
145
|
`- 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}`,
|
|
149
146
|
`- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,
|
|
147
|
+
...outcome.unreviewedPaths.length === 0 ? [] : [`- Carried forward: ${outcome.unreviewedPaths.length} unreviewed path(s) retried automatically on the next run (reviewer-side gap, not a code defect)`],
|
|
150
148
|
...modelLabel === void 0 ? [] : [`- Model: \`${modelLabel}\``],
|
|
151
|
-
...outcome.usage === void 0 ? [] : [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out
|
|
149
|
+
...outcome.usage === void 0 ? [] : [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out`],
|
|
152
150
|
...outcome.published === void 0 ? ["- Dry run: nothing posted"] : [`- Posted: ${outcome.published.url}`]
|
|
153
151
|
];
|
|
154
152
|
const skip = (reason) => Effect.gen(function* () {
|
|
@@ -168,26 +166,37 @@ const resolveRunUrl = Effect.fn("resolveRunUrl")(function* () {
|
|
|
168
166
|
return `${yield* Config.string("GITHUB_SERVER_URL").pipe(Config.withDefault("https://github.com"))}/${repository}/actions/runs/${runId}`;
|
|
169
167
|
});
|
|
170
168
|
const blockingReasons = (input) => [...input.findings.filter((finding) => finding.severity === "blocking").map((finding) => `blocking finding: ${finding.title}`), ...input.concerns.filter((concern) => concern.severity === "blocking").map((concern) => `blocking concern: ${concern.title}`)];
|
|
171
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Host-derived check conclusion; model verdict prose cannot weaken it.
|
|
171
|
+
*
|
|
172
|
+
* Blocking code findings outrank machinery gaps — they are the actionable
|
|
173
|
+
* signal. A machinery gap (incomplete input, unsettled passes) concludes
|
|
174
|
+
* `incomplete` with reasons that explicitly say the failure is reviewer-side
|
|
175
|
+
* uncertainty carried forward for retry, never an invitation to change code.
|
|
176
|
+
* The flat reviewer's constant `unverified` assurance is not a gap.
|
|
177
|
+
*/
|
|
172
178
|
const concludeReviewOutcome = (outcome) => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
const reasons = blockingReasons({
|
|
179
|
+
const machinery = [];
|
|
180
|
+
if (outcome.inputCoverage.status === "incomplete" || outcome.assurance.status === "incomplete") {
|
|
181
|
+
machinery.push(outcome.unreviewedPaths.length > 0 ? `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` : "review infrastructure did not settle — a reviewer-side gap, not a code defect");
|
|
182
|
+
if (outcome.inputCoverage.status === "incomplete") machinery.push(...outcome.inputCoverage.reasons);
|
|
183
|
+
if (outcome.assurance.status === "incomplete") machinery.push(...outcome.assurance.reasons);
|
|
184
|
+
}
|
|
185
|
+
const blocking = blockingReasons({
|
|
182
186
|
findings: outcome.activeFindings,
|
|
183
187
|
concerns: outcome.activeConcerns
|
|
184
188
|
});
|
|
185
|
-
|
|
189
|
+
if (blocking.length > 0) return {
|
|
186
190
|
conclusion: "blocking",
|
|
187
|
-
reasons
|
|
188
|
-
}
|
|
191
|
+
reasons: [...blocking, ...machinery]
|
|
192
|
+
};
|
|
193
|
+
if (machinery.length > 0) return {
|
|
194
|
+
conclusion: "incomplete",
|
|
195
|
+
reasons: machinery
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
189
198
|
conclusion: "success",
|
|
190
|
-
reasons
|
|
199
|
+
reasons: []
|
|
191
200
|
};
|
|
192
201
|
};
|
|
193
202
|
const concludeReviewState = (state) => {
|
|
@@ -274,7 +283,7 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
|
|
|
274
283
|
failure: void 0
|
|
275
284
|
})
|
|
276
285
|
}));
|
|
277
|
-
const equivalentPatchState = (options.reviewMode ?? "incremental") === "incremental" && options.skipUnchanged !== false && recovered.state !== void 0 && currentFingerprint !== void 0 && validateReviewState(recovered.state, metadata, profileFingerprint) === void 0 && recovered.state.acceptedScopeFingerprint === currentFingerprint ? recovered.state : void 0;
|
|
286
|
+
const equivalentPatchState = (options.reviewMode ?? "incremental") === "incremental" && options.skipUnchanged !== false && recovered.state !== void 0 && recovered.state.settled && currentFingerprint !== void 0 && validateReviewState(recovered.state, metadata, profileFingerprint) === void 0 && recovered.state.acceptedScopeFingerprint === currentFingerprint ? recovered.state : void 0;
|
|
278
287
|
if (equivalentPatchState !== void 0) return yield* skipCoveredReview({
|
|
279
288
|
repository: target.repository,
|
|
280
289
|
pullRequestNumber: target.number,
|
|
@@ -321,7 +330,7 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
|
|
|
321
330
|
}),
|
|
322
331
|
stateAuthenticator
|
|
323
332
|
};
|
|
324
|
-
if (options.skipUnchanged !== false && selection.mode === "incremental" && selection.files.length === 0 && selection.priorState !== void 0) return yield* skipCoveredReview({
|
|
333
|
+
if (options.skipUnchanged !== false && selection.mode === "incremental" && selection.files.length === 0 && selection.priorState !== void 0 && selection.priorState.settled) return yield* skipCoveredReview({
|
|
325
334
|
repository: target.repository,
|
|
326
335
|
pullRequestNumber: target.number,
|
|
327
336
|
reason: "no changed review scope since the last settled review head",
|
|
@@ -418,7 +427,6 @@ const reviewActionProgram = Effect.gen(function* () {
|
|
|
418
427
|
};
|
|
419
428
|
const harness = {
|
|
420
429
|
post: inputs.post,
|
|
421
|
-
failOn: inputs.failOn,
|
|
422
430
|
skipUnchanged: inputs.skipUnchanged,
|
|
423
431
|
reviewMode: inputs.reviewMode,
|
|
424
432
|
retireStaleReviews: inputs.retireStaleReviews,
|
|
@@ -456,6 +464,6 @@ const PrReviewAction = {
|
|
|
456
464
|
main
|
|
457
465
|
};
|
|
458
466
|
//#endregion
|
|
459
|
-
export {
|
|
467
|
+
export { GuidanceFileUnreadable, InvalidMaxDurationInput, MAX_GUIDANCE_FILE_CHARS, PrReviewAction, ReviewCheckConclusion, ReviewGateFailed, concludeReviewOutcome, main, resolveActionInputs, resolveGuidance, reviewActionProgram, runReviewAction, writeActionOutputs, writeStepSummary };
|
|
460
468
|
|
|
461
469
|
//# sourceMappingURL=action.mjs.map
|