@effect-agent/pr-review 0.1.0-beta.20 → 0.1.0-beta.22

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  # @effect-agent/pr-review
2
2
 
3
3
  A bounded, fail-closed GitHub pull-request reviewer built on the effect-agent
4
- public surface. One read-only Agent reviews a pull request through typed
5
- ports; the host validates every finding anchor against the real diff and
6
- posts one review after the run settles.
4
+ public surface. Read-only Agents review host-partitioned pull-request evidence
5
+ through typed ports; the host validates every finding anchor against the real
6
+ diff and posts one review after the run settles.
7
7
 
8
8
  **Deployment class E (ephemeral).** One `AgentRuntime.run` per invocation, no
9
9
  durability claim, and review posting is never exactly-once: a failed or
@@ -58,11 +58,22 @@ configurable: model output is untrusted input, so finding anchors are
58
58
  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
- `ReviewPublisher` port after the run settles. `PrReview.makeFanOut` builds
62
- the delegating variant bounded per-unit child reviewers (S1 attached
63
- ephemeral delegation) merged under the same output contract and the same
64
- publication path; the shared `guidance` and `maxFindings` shape the
65
- coordinator's merge as well as the children.
61
+ `ReviewPublisher` port after the run settles. `PrReview.makeFanOut` builds the
62
+ assured delegating variant. The host deterministically partitions bounded
63
+ evidence, classifies high-risk units, requires a general discovery pass for
64
+ every unit, adds a fresh specialist discovery pass for every unit, and uses
65
+ deterministic host-classified risk categories as explicit focus labels. A
66
+ fresh verifier must confirm or reject every discovered candidate. These are
67
+ S1 attached ephemeral children using Effect structured concurrency. Verifiers
68
+ receive the exact candidate claims and the complete bounded unit, including
69
+ neighboring evidence that can falsify a locally plausible claim; they do not
70
+ receive discovery reasoning.
71
+ The host reconstructs publishable findings and concerns from exact confirmed
72
+ candidate IDs, deduplicates byte-identical cross-pass claims in deterministic
73
+ plan order, and requires every delegation declaration to be consumed by one
74
+ exact planned request. Neither a discovery child nor the coordinator can publish
75
+ an unsupported or invented finding. Shared `guidance` reaches both discovery
76
+ and verification; `maxFindings` remains a host-enforced publication cap.
66
77
 
67
78
  GitHub may omit the `patch` field for large textual files as well as binary
68
79
  files. The GitHub source recovers a missing patch by reading bounded, strict
@@ -73,6 +84,35 @@ report defects as review-body concerns. Invalid UTF-8, binary NUL content,
73
84
  missing sides, files beyond the per-side read bound, and complete B/H evidence
74
85
  beyond the model-facing render bound remain explicit coverage gaps.
75
86
 
87
+ ## Assurance model
88
+
89
+ The public result deliberately separates two claims:
90
+
91
+ - **Input coverage** says every required path in the selected scope was
92
+ assigned every deterministic bounded evidence shard and separately names
93
+ partially assigned and unassigned paths, the exact unassigned-shard count
94
+ with a bounded identifier sample, undiffable paths, source truncation, or
95
+ over-capacity paths. A large textual diff is split across
96
+ shards and units instead of being called partial merely for exceeding one
97
+ prompt chunk. Input coverage does not say the model understood the evidence.
98
+ - **Review assurance** says every configured general discovery pass, required
99
+ independent specialist pass, and candidate-verification batch settled
100
+ exactly. It reports discovered, confirmed, rejected, and unsettled
101
+ candidate counts plus failed pass IDs.
102
+
103
+ `settled` assurance means the configured work completed; it never means the
104
+ review is exhaustive or the pull request is defect-free. Risk classification
105
+ is deterministic host policy over paths and bounded text and intentionally
106
+ favors redundant work, but it cannot recognize every semantically risky
107
+ change. The specialist pass is context-independent redundancy, not a claim of
108
+ provider or model diversity. Running it for every unit prevents classifier
109
+ silence from suppressing scrutiny; the category labels still cannot prove
110
+ that every semantic risk was recognized. The legacy aggregate `coverage` field remains
111
+ for compatibility; new hosts and UI use `inputCoverage` and `assurance`.
112
+ The flat reviewer has path-input accounting but no independent verifier, so
113
+ its assurance is `unverified` and the Action check cannot report success from
114
+ that shape.
115
+
76
116
  ## What a posted review looks like
77
117
 
78
118
  The body opens with a host-derived callout tier — `[!CAUTION]` when any
@@ -81,7 +121,9 @@ for nits, `✅` for a clean approval — computed from the validated severities,
81
121
  never from model prose. Directly under it, one host-derived stats line names
82
122
  the changeset size (`N files (+adds / −dels)`), the severity tally, and a
83
123
  deterministic 1–5 review-effort estimate computed from the changeset shape
84
- alone. Below the summary, a collapsed **📝 Walkthrough** table carries the
124
+ alone. Fan-out reviews add a second host-derived line naming input assignment,
125
+ discovery/specialist/verification settlement, and candidate dispositions.
126
+ Below the summary, a collapsed **📝 Walkthrough** table carries the
85
127
  model's one-sentence per-file change summaries — walkthrough paths are
86
128
  validated like finding anchors, so entries naming files outside the changeset
87
129
  are dropped. Under fan-out the walkthrough is additionally host-verified
@@ -129,14 +171,15 @@ ordinary gate.
129
171
 
130
172
  ## Incremental Action reviews
131
173
 
132
- A completely covered posted Action review carries bounded, versioned,
133
- HMAC-authenticated continuity state:
134
- the exact PR/base/head lineage, profile and accepted-scope fingerprints, and
174
+ A posted Action review with complete input coverage and settled review
175
+ assurance carries bounded, versioned, HMAC-authenticated continuity state:
176
+ the exact PR/base/head lineage, profile and settled-scope fingerprints, and
135
177
  the still-unresolved findings and concerns. A later Action run validates the
136
178
  state and reviews the GitHub comparison from that reviewed head to the
137
- current head, not the complete base...HEAD diff. Unchanged accepted scope is
179
+ current head, not the complete base...HEAD diff. Unchanged settled scope is
138
180
  not sent back to the model; unchanged unresolved findings remain active;
139
- changed or reverted paths invalidate their prior findings. Non-anchored
181
+ changed or reverted paths invalidate their prior findings and receive fresh
182
+ discovery and verification. Non-anchored
140
183
  concerns are carried conservatively until a full audit because they cannot be
141
184
  mapped safely to one path.
142
185
 
@@ -150,10 +193,11 @@ and comparison checks are fail-closed for scope selection: missing, stale,
150
193
  incompatible, or truncated state/comparisons produce a visible full-diff
151
194
  fallback. An ancestor base advance remains incremental and adds overlapping
152
195
  PR paths as affected context; a materially changed base lineage falls back to
153
- full unless the authenticated accepted-scope fingerprint still matches. That
196
+ full unless the authenticated settled-scope fingerprint still matches. The
197
+ schema field retains the compatibility name `acceptedScopeFingerprint`. That
154
198
  fingerprint hashes the ignore-filtered effective diff, patchless base/head
155
199
  evidence, PR framing, and review-shaping profile while excluding commit IDs,
156
- base ancestry, and unified-diff hunk coordinates. Re-running the same covered
200
+ base ancestry, and unified-diff hunk coordinates. Re-running the same settled
157
201
  head or a patch-equivalent rebase skips model execution by default while
158
202
  preserving its stored blocking/success conclusion and posting no duplicate
159
203
  review comments. Missing/corrupt state and any fingerprint or profile mismatch
@@ -175,8 +219,9 @@ schema-branded and capped at 24,000 characters. If signing fails or state
175
219
  exceeds that bound, the completed review is posted without continuity state
176
220
  and with a bounded warning, so the next run safely performs a full review.
177
221
 
178
- `review-mode: final` is the explicit bounded merge-readiness audit. It reviews
179
- the full current PR diff and resets the incremental baseline; normal
222
+ `review-mode: final` is the explicit bounded merge-readiness audit. It ignores
223
+ prior assurance state, plans fresh discovery over the full current PR
224
+ diff, verifies new candidates, and resets the incremental baseline; normal
180
225
  `synchronize` events use `incremental` and do not perform this audit.
181
226
  Because equivalence is based on the textual review surface, a base change that
182
227
  alters runtime meaning without changing the diff/context is not detectable;
@@ -188,7 +233,8 @@ request `final` mode (or disable unchanged skipping) for that case.
188
233
  supporting a committed review-profile document via `guidance-file` (this
189
234
  repository's own profile lives at `.github/review-guidance.md`)
190
235
  (`action/` at the repo root) — `uses` it with an API-key secret and nothing
191
- else. While a run executes it maintains one sticky, fail-open "review in
236
+ else. Fan-out is the Action default because the flat compatibility shape
237
+ has no independent verifier and cannot settle review assurance. While a run executes it maintains one sticky, fail-open "review in
192
238
  progress" comment updated in place with the settled outcome
193
239
  (`progress-comment` input, default on; at-least-once with generation-fenced
194
240
  writes and best-effort duplicate cleanup — strict single-comment behavior
@@ -198,7 +244,9 @@ request `final` mode (or disable unchanged skipping) for that case.
198
244
  resolution, typed draft/non-PR skips, bounded range selection, step
199
245
  outputs, and conservative check gate) to harness your own `reviewer.run`;
200
246
  pass `progressComment: true` to opt a custom harness into the sticky
201
- progress comment.
247
+ progress comment. A custom fingerprint-only harness remains source-compatible
248
+ but cannot skip model work: unchanged skipping requires the profile fingerprint
249
+ and authenticated state that prove the prior configured work settled.
202
250
  - **CLI**: `bun src/cli.ts --repo owner/name --pr 123 [--post] [--provider anthropic] [--fan-out]`
203
251
  (also exported as the `./cli` entry).
204
252
 
@@ -218,9 +266,17 @@ bytes or characters is refused typed; patchless content is reviewable only
218
266
  when its complete B/H rendering fits 220k characters. The changeset surface is
219
267
  bounded at 300 files:
220
268
  files beyond the bound are not fetched, and the review body reports
221
- `Reviewed N of M changed files` instead of claiming completeness. Fan-out
222
- capacity overflow is reported in the review summary, never dropped. Any
223
- blocking active finding fails the Action check. Any required-file coverage
224
- gap paths with neither a patch nor bounded textual fallback, unassigned
225
- paths, failed units (including policy exhaustion), truncation, or
226
- coordinator/run failure is non-success rather than green.
269
+ `Input exposed N of M changed files` instead of claiming completeness.
270
+ Fan-out capacity overflow is reported, never dropped. Every fan-out unit has
271
+ at most 12 files, 12 complete evidence shards, and 240,000 evidence characters;
272
+ one path may span multiple shards or units when necessary. If the eight-unit
273
+ plan capacity is exhausted, every affected path and the exact shard count are
274
+ reported, with a deterministic identifier sample bounded to one plan's capacity;
275
+ at most eight units produce at most 24 attached children (general and
276
+ specialist discovery plus one verification batch per unit), with child
277
+ concurrency capped at four. Any blocking active finding fails the Action
278
+ check. Any input gap—including an unassigned evidence shard—failed or
279
+ exhausted configured pass, mismatched candidate batch, or unsettled
280
+ verification is non-success rather than green. A settled
281
+ clean result is evidence that these bounded passes completed, not proof that
282
+ no defect exists.
package/dist/action.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Et as ReviewMode, G as GitHubApiFailure, In as PullRequestMetadata, J as PriorReviews, Ln as PullRequestSource, Rn as PullRequestSourceFailure, Vn as ChangedFile, X as ReviewPublisher, jt as ReviewStateAuthenticator, st as ReviewRetirementHost } from "./fan-out-CQFA-o0v.mjs";
1
+ import { $t as ReviewMode, Ft as ReviewRetirementHost, St as PriorReviews, br as PullRequestSource, in as ReviewStateAuthenticator, wr as ChangedFile, wt as ReviewPublisher, xr as PullRequestSourceFailure, yr as PullRequestMetadata, yt as GitHubApiFailure } from "./fan-out-n-00ppWr.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";
@@ -94,11 +94,17 @@ type ReviewActionResult = {
94
94
  readonly _tag: "Completed";
95
95
  readonly outcome: ReviewRunOutcome;
96
96
  };
97
- /** The reviewer surface the action harness drives. `PrReview.make` and
98
- * `PrReview.makeFanOut` provide the state-selection effects; the legacy
99
- * fingerprint field remains for source compatibility with custom harnesses. */
97
+ /**
98
+ * The reviewer surface the action harness drives. `PrReview.make` and
99
+ * `PrReview.makeFanOut` provide the state-selection effects. A fingerprint is
100
+ * skip authority only when a profile fingerprint and authenticated review
101
+ * state bind it to settled assurance; a fingerprint-only custom harness
102
+ * remains source-compatible but deliberately runs instead of claiming green
103
+ * assurance from unauthenticated prior-review text.
104
+ */
100
105
  interface HarnessedReviewer<E, R, FingerprintE, FingerprintR> {
101
106
  readonly run: (runOptions?: RunReviewOptions) => Effect.Effect<ReviewRunOutcome, E, R>;
107
+ /** Current effective changeset fingerprint; not standalone skip authority. */
102
108
  readonly fingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;
103
109
  readonly profileFingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;
104
110
  readonly snapshot?: Effect.Effect<{
@@ -159,7 +165,7 @@ declare const reviewActionProgram: Effect.Effect<{
159
165
  } | {
160
166
  _tag: "Completed";
161
167
  outcome: ReviewRunOutcome;
162
- }, 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>;
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-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>;
163
169
  /** Run the packaged action program on Node; the bundled action entrypoint. */
164
170
  declare const main: () => void;
165
171
  /** The packaged GitHub Actions surface. */
@@ -189,7 +195,7 @@ declare const PrReviewAction: {
189
195
  } | {
190
196
  _tag: "Completed";
191
197
  outcome: ReviewRunOutcome;
192
- }, 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>;
193
199
  readonly main: typeof main;
194
200
  };
195
201
  //#endregion
package/dist/action.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { A as ReviewStateAuthenticator, B as selectedPullRequestSourceLayer, G as webCryptoReviewStateAuthenticatorLayer, Sn as normalizeRepoRelativePath, T as ReviewHeadComparison, U as unavailableReviewStateAuthenticatorLayer, W as validateReviewState, a as PriorReviews, w as ReviewExecutionContext, x as retireStaleReviews, yn as PullRequestSource, z as selectReviewRange } from "./github-DnenG3be.mjs";
2
- import { C as PrReview, H as InvalidEffortInput, O as fanOutReviewBudgetLimits, W as parseEffortPosition, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, g as ReviewProgressReporter, k as reviewBudgetLimits, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, p as readGitHubEvent, s as makeAnthropicReviewModel } from "./providers-BE83_Tfo.mjs";
1
+ import { $n as normalizeRepoRelativePath, A as ReviewStateAuthenticator, B as selectedPullRequestSourceLayer, G as webCryptoReviewStateAuthenticatorLayer, T as ReviewHeadComparison, U as unavailableReviewStateAuthenticatorLayer, W as validateReviewState, Xn as PullRequestSource, a as PriorReviews, w as ReviewExecutionContext, x as retireStaleReviews, z as selectReviewRange } from "./github-BgtP7Rdv.mjs";
2
+ import { C as PrReview, O as fanOutReviewBudgetLimits, U as parseEffortPosition, V as InvalidEffortInput, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, g as ReviewProgressReporter, k as reviewBudgetLimits, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, p as readGitHubEvent, s as makeAnthropicReviewModel } from "./providers-BguZK4B_.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";
@@ -42,7 +42,7 @@ const resolveActionInputs = Effect.fn("resolveActionInputs")(function* () {
42
42
  if (maxDurationMinutes !== void 0 && maxDurationMinutes <= 0) return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });
43
43
  const post = yield* Config.boolean("PR_REVIEW_POST").pipe(Config.withDefault(true));
44
44
  const applyVerdict = yield* Config.boolean("PR_REVIEW_APPLY_VERDICT").pipe(Config.withDefault(false));
45
- const fanOut = yield* Config.boolean("PR_REVIEW_FAN_OUT").pipe(Config.withDefault(false));
45
+ const fanOut = yield* Config.boolean("PR_REVIEW_FAN_OUT").pipe(Config.withDefault(true));
46
46
  const guidance = yield* Config.option(Config.nonEmptyString("PR_REVIEW_GUIDANCE"));
47
47
  const guidanceFile = yield* Config.option(Config.nonEmptyString("PR_REVIEW_GUIDANCE_FILE"));
48
48
  const ignoreRaw = yield* Config.string("PR_REVIEW_IGNORE").pipe(Config.withDefault(""));
@@ -129,6 +129,8 @@ const outcomeOutputs = (outcome, conclusion) => [
129
129
  ["skipped", "false"],
130
130
  ["conclusion", conclusion],
131
131
  ["verdict", outcome.review.verdict],
132
+ ["input-coverage", outcome.inputCoverage.status],
133
+ ["review-assurance", outcome.assurance.status],
132
134
  ["coverage", outcome.coverage.status],
133
135
  ["review-mode", outcome.reviewMode ?? "full"],
134
136
  ["review-reason", outcome.reviewReason ?? "direct full review"],
@@ -142,7 +144,8 @@ const outcomeSummary = (outcome, modelLabel, conclusion) => [
142
144
  "### Pull-request review",
143
145
  `- Check conclusion: **${conclusion}**`,
144
146
  `- Verdict: **${outcome.review.verdict}**`,
145
- `- Coverage: **${outcome.coverage.status}** · scope: ${outcome.reviewMode ?? "full"}`,
147
+ `- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? "full"}`,
148
+ `- 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}`,
146
149
  `- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,
147
150
  ...modelLabel === void 0 ? [] : [`- Model: \`${modelLabel}\``],
148
151
  ...outcome.usage === void 0 ? [] : [`- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out${outcome.usageScope === "coordinator" ? " (coordinator)" : ""}`],
@@ -167,9 +170,13 @@ const resolveRunUrl = Effect.fn("resolveRunUrl")(function* () {
167
170
  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}`)];
168
171
  /** Host-derived check conclusion; model verdict prose cannot weaken it. */
169
172
  const concludeReviewOutcome = (outcome) => {
170
- if (outcome.coverage.status === "incomplete") return {
173
+ if (outcome.inputCoverage.status === "incomplete") return {
171
174
  conclusion: "incomplete",
172
- reasons: outcome.coverage.reasons
175
+ reasons: outcome.inputCoverage.reasons
176
+ };
177
+ if (outcome.assurance.status !== "settled") return {
178
+ conclusion: "incomplete",
179
+ reasons: outcome.assurance.reasons.length > 0 ? outcome.assurance.reasons : ["configured discovery and verification work did not settle"]
173
180
  };
174
181
  const reasons = blockingReasons({
175
182
  findings: outcome.activeFindings,
@@ -204,6 +211,8 @@ const skipCoveredReview = Effect.fn("skipCoveredReview")(function* (input) {
204
211
  ["skip-reason", input.reason],
205
212
  ...input.fingerprint === void 0 ? [] : [["fingerprint", input.fingerprint]],
206
213
  ["conclusion", result.conclusion],
214
+ ["input-coverage", "complete"],
215
+ ["review-assurance", "settled"],
207
216
  ["coverage", "complete"],
208
217
  ["review-mode", "incremental"]
209
218
  ]);
@@ -269,7 +278,7 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
269
278
  if (equivalentPatchState !== void 0) return yield* skipCoveredReview({
270
279
  repository: target.repository,
271
280
  pullRequestNumber: target.number,
272
- reason: equivalentPatchState.reviewedHeadSha === metadata.headSha ? "the current head already has complete stored review coverage" : "the effective pull-request patch is unchanged since the last complete review",
281
+ reason: equivalentPatchState.reviewedHeadSha === metadata.headSha ? "the current head already has settled stored review assurance" : "the effective pull-request patch is unchanged since the last settled review",
273
282
  state: equivalentPatchState,
274
283
  fingerprint: currentFingerprint
275
284
  });
@@ -315,28 +324,9 @@ const runReviewAction = (reviewer, options = {}) => Effect.gen(function* () {
315
324
  if (options.skipUnchanged !== false && selection.mode === "incremental" && selection.files.length === 0 && selection.priorState !== void 0) return yield* skipCoveredReview({
316
325
  repository: target.repository,
317
326
  pullRequestNumber: target.number,
318
- reason: "no changed review scope since the last successfully reviewed head",
327
+ reason: "no changed review scope since the last settled review head",
319
328
  state: selection.priorState
320
329
  });
321
- } else if (options.skipUnchanged !== false && reviewer.fingerprint !== void 0) {
322
- const current = yield* reviewer.fingerprint;
323
- const latest = yield* (options.priorReviews ?? (yield* PriorReviews)).latestFingerprint.pipe(Effect.orElseSucceed(() => Option.none()));
324
- if (Option.isSome(latest) && latest.value === current) {
325
- const reason = "changeset unchanged since the last review";
326
- yield* Console.log(`Skipping review of ${target.repository}#${target.number}: ${reason}.`);
327
- yield* writeActionOutputs([
328
- ["skipped", "true"],
329
- ["skip-reason", reason],
330
- ["fingerprint", current],
331
- ["conclusion", "success"],
332
- ["coverage", "complete"]
333
- ]);
334
- yield* writeStepSummary(["### Pull-request review skipped", `- Reason: ${reason}`]);
335
- return {
336
- _tag: "Skipped",
337
- reason
338
- };
339
- }
340
330
  }
341
331
  yield* Console.log(`Reviewing ${target.repository}#${target.number} (${options.post === false ? "dry run" : "posting"})...`);
342
332
  const runUrl = yield* resolveRunUrl();
@@ -1 +1 @@
1
- {"version":3,"file":"action.mjs","names":[],"sources":["../src/action.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from \"effect\";\nimport { BudgetExceeded, UsageBudgetLimits } from \"effect-agent\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { readGitHubEvent, resolveReviewTarget, gitHubReviewLayers } from \"./internal/github-env.ts\";\nimport { PriorReviews } from \"./internal/github.ts\";\nimport { compactReviewLoggingLayer } from \"./internal/logging.ts\";\nimport { ReviewProgressReporter } from \"./internal/progress.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { retireStaleReviews } from \"./internal/retirement.ts\";\nimport {\n ReviewExecutionContext,\n ReviewHeadComparison,\n ReviewStateAuthenticator,\n type ReviewMode,\n type ReviewState,\n selectReviewRange,\n selectedPullRequestSourceLayer,\n unavailableReviewStateAuthenticatorLayer,\n validateReviewState,\n webCryptoReviewStateAuthenticatorLayer,\n} from \"./internal/review-state.ts\";\nimport { fanOutReviewBudgetLimits, reviewBudgetLimits } from \"./internal/run.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\nimport { normalizeRepoRelativePath, PullRequestSource } from \"./internal/source.ts\";\n\n// ---------------------------------------------------------------------------\n// The GitHub Actions entrypoint (deployment class E: one bounded ephemeral\n// run, no exactly-once posting). Bounded continuity state travels in GitHub\n// review bodies and is validated before reuse. Inputs arrive as\n// PR_REVIEW_* environment variables set by the action manifest; the target\n// pull request comes from the standard Actions event environment. A non-PR\n// or draft event is a typed skip — green job, nothing posted. Publication\n// happens only after the run settles, so a failed or truncated run posts\n// nothing.\n// ---------------------------------------------------------------------------\n\n/** Deprecated compatibility input; host-derived conclusions are unconditional. */\nexport const FailOnPolicy = Schema.Literals([\"never\", \"request-changes\"]);\nexport type FailOnPolicy = typeof FailOnPolicy.Type;\n\nexport const ReviewCheckConclusion = Schema.Literals([\"success\", \"blocking\", \"incomplete\"]);\nexport type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;\n\n/** The host-derived blocking or incomplete conclusion failed the check. */\nexport class ReviewGateFailed extends Schema.TaggedError<ReviewGateFailed>()(\"ReviewGateFailed\", {\n conclusion: Schema.Literals([\"blocking\", \"incomplete\"]),\n reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1_000))).check(\n Schema.isMinLength(1),\n ),\n}) {\n override get message() {\n return `Review check concluded '${this.conclusion}': ${this.reasons.join(\"; \")}`;\n }\n}\n\n/** A configured max-duration that cannot bound a run; configuration faults fail loudly. */\nexport class InvalidMaxDurationInput extends Schema.TaggedError<InvalidMaxDurationInput>()(\n \"InvalidMaxDurationInput\",\n {\n minutes: Schema.Int,\n },\n) {\n override get message() {\n return `Invalid max-duration-minutes '${this.minutes}': expected a positive number of minutes.`;\n }\n}\n\n/** Everything the packaged action reads from its environment. */\nexport interface ResolvedActionInputs {\n readonly provider: ReviewProvider;\n readonly model: string | undefined;\n readonly effort: EffortPosition | undefined;\n readonly post: boolean;\n readonly applyVerdict: boolean;\n readonly fanOut: boolean;\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n readonly ignore: ReadonlyArray<string>;\n readonly maxFindings: number | undefined;\n readonly maxDurationMinutes: number | undefined;\n readonly reviewMode: ReviewMode;\n /** Deprecated compatibility input; conclusions are always conservative. */\n readonly failOn: FailOnPolicy;\n readonly skipUnchanged: boolean;\n readonly retireStaleReviews: boolean;\n readonly progressComment: boolean;\n}\n\n/** Read the PR_REVIEW_* input surface (all optional, all defaulted). */\nexport const resolveActionInputs = Effect.fn(\"resolveActionInputs\")(function* () {\n const provider = yield* Config.literals([\"openai\", \"anthropic\"], \"PR_REVIEW_PROVIDER\").pipe(\n Config.withDefault<ReviewProvider>(DEFAULT_PROVIDER),\n );\n const model = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_MODEL\"));\n const effortRaw = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_EFFORT\"));\n let effort: EffortPosition | undefined;\n if (Option.isSome(effortRaw)) {\n effort = parseEffortPosition(effortRaw.value);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw.value });\n }\n }\n const maxDurationMinutes = Option.getOrUndefined(\n yield* Config.option(Config.int(\"PR_REVIEW_MAX_DURATION_MINUTES\")),\n );\n if (maxDurationMinutes !== undefined && maxDurationMinutes <= 0) {\n return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });\n }\n const post = yield* Config.boolean(\"PR_REVIEW_POST\").pipe(Config.withDefault(true));\n const applyVerdict = yield* Config.boolean(\"PR_REVIEW_APPLY_VERDICT\").pipe(\n Config.withDefault(false),\n );\n const fanOut = yield* Config.boolean(\"PR_REVIEW_FAN_OUT\").pipe(Config.withDefault(false));\n const guidance = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE\"));\n const guidanceFile = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE_FILE\"));\n const ignoreRaw = yield* Config.string(\"PR_REVIEW_IGNORE\").pipe(Config.withDefault(\"\"));\n const maxFindings = yield* Config.option(Config.int(\"PR_REVIEW_MAX_FINDINGS\"));\n const reviewMode = yield* Config.literals([\"incremental\", \"final\"], \"PR_REVIEW_MODE\").pipe(\n Config.withDefault<ReviewMode>(\"incremental\"),\n );\n const failOn = yield* Config.literals([\"never\", \"request-changes\"], \"PR_REVIEW_FAIL_ON\").pipe(\n Config.withDefault<FailOnPolicy>(\"never\"),\n );\n const skipUnchanged = yield* Config.boolean(\"PR_REVIEW_SKIP_UNCHANGED\").pipe(\n Config.withDefault(true),\n );\n const retireStaleReviews = yield* Config.boolean(\"PR_REVIEW_RETIRE_STALE_REVIEWS\").pipe(\n Config.withDefault(true),\n );\n const progressComment = yield* Config.boolean(\"PR_REVIEW_PROGRESS_COMMENT\").pipe(\n Config.withDefault(true),\n );\n return {\n provider,\n model: Option.getOrUndefined(model),\n effort,\n post,\n applyVerdict,\n fanOut,\n guidance: Option.getOrUndefined(guidance),\n guidanceFile: Option.getOrUndefined(guidanceFile),\n ignore: ignoreRaw\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(maxFindings),\n maxDurationMinutes,\n reviewMode,\n failOn,\n skipUnchanged,\n retireStaleReviews,\n progressComment,\n } satisfies ResolvedActionInputs;\n});\n\n/** A guidance file larger than this is refused, never silently truncated. */\nexport const MAX_GUIDANCE_FILE_CHARS = 20_000;\n\n/** A configured guidance file could not be used; configuration faults fail loudly. */\nexport class GuidanceFileUnreadable extends Schema.TaggedError<GuidanceFileUnreadable>()(\n \"GuidanceFileUnreadable\",\n {\n path: Schema.String,\n reason: Schema.String,\n },\n) {\n override get message() {\n return `Cannot use guidance file '${this.path}': ${this.reason}`;\n }\n}\n\n/**\n * Resolve the effective review guidance: the committed guidance file (a\n * repository-owned review profile) first, with any inline guidance appended.\n * A configured-but-unreadable file fails typed — a review silently running\n * without its profile would be worse than a red job.\n */\nexport const resolveGuidance = Effect.fn(\"resolveGuidance\")(function* (inputs: {\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n}) {\n const filePath = inputs.guidanceFile;\n if (filePath === undefined) return inputs.guidance;\n // The profile path is operator configuration, but it stays fail-closed\n // like every other path in this package: workspace-relative only, and the\n // size bound is checked before the file is read into memory.\n const relative = yield* normalizeRepoRelativePath(filePath).pipe(\n Effect.mapError((violation) =>\n GuidanceFileUnreadable.make({ path: filePath, reason: violation.reason }),\n ),\n );\n const fs = yield* FileSystem.FileSystem;\n const unreadable = (error: { readonly _tag: string; readonly message: string }) =>\n GuidanceFileUnreadable.make({\n path: relative,\n reason: `${error._tag}: ${error.message}`.slice(0, 2_048),\n });\n const stat = yield* fs.stat(relative).pipe(Effect.mapError(unreadable));\n const oversized = GuidanceFileUnreadable.make({\n path: relative,\n reason: `File is larger than the ${MAX_GUIDANCE_FILE_CHARS}-character guidance bound.`,\n });\n if (stat.size > BigInt(MAX_GUIDANCE_FILE_CHARS) * 4n) {\n return yield* oversized;\n }\n const content = yield* fs.readFileString(relative).pipe(Effect.mapError(unreadable));\n if (content.length > MAX_GUIDANCE_FILE_CHARS) {\n return yield* oversized;\n }\n const combined = [content.trim(), inputs.guidance ?? \"\"]\n .filter((part) => part.length > 0)\n .join(\"\\n\");\n return combined.length > 0 ? combined : undefined;\n});\n\n/** One step-output line; values must be single-line by construction. */\nconst outputLine = (name: string, value: string): string =>\n `${name}=${value.replaceAll(\"\\n\", \" \").slice(0, 1_000)}\\n`;\n\n/** Append step outputs to GITHUB_OUTPUT when present (no-op locally). */\nexport const writeActionOutputs = Effect.fn(\"writeActionOutputs\")(function* (\n entries: ReadonlyArray<readonly [name: string, value: string]>,\n) {\n const outputPath = yield* Config.string(\"GITHUB_OUTPUT\").pipe(Config.withDefault(\"\"));\n if (outputPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(\n outputPath,\n entries.map(([name, value]) => outputLine(name, value)).join(\"\"),\n {\n flag: \"a\",\n },\n );\n});\n\n/** Append markdown to the GITHUB_STEP_SUMMARY report when present (no-op locally). */\nexport const writeStepSummary = Effect.fn(\"writeStepSummary\")(function* (\n lines: ReadonlyArray<string>,\n) {\n const summaryPath = yield* Config.string(\"GITHUB_STEP_SUMMARY\").pipe(Config.withDefault(\"\"));\n if (summaryPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(summaryPath, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n});\n\nconst outcomeOutputs = (\n outcome: ReviewRunOutcome,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<readonly [string, string]> => [\n [\"skipped\", \"false\"],\n [\"conclusion\", conclusion],\n [\"verdict\", outcome.review.verdict],\n [\"coverage\", outcome.coverage.status],\n [\"review-mode\", outcome.reviewMode ?? \"full\"],\n [\"review-reason\", outcome.reviewReason ?? \"direct full review\"],\n [\"inline-comments\", String(outcome.plan.comments.length)],\n [\"demoted-findings\", String(outcome.plan.demoted.length)],\n [\"concerns\", String(outcome.review.concerns?.length ?? 0)],\n ...(outcome.usage === undefined\n ? []\n : ([\n [\"input-tokens\", String(outcome.usage.inputTokens)],\n [\"output-tokens\", String(outcome.usage.outputTokens)],\n ] as const)),\n [\"review-url\", outcome.published?.url ?? \"\"],\n];\n\nconst outcomeSummary = (\n outcome: ReviewRunOutcome,\n modelLabel: string | undefined,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<string> => [\n \"### Pull-request review\",\n `- Check conclusion: **${conclusion}**`,\n `- Verdict: **${outcome.review.verdict}**`,\n `- Coverage: **${outcome.coverage.status}** · scope: ${outcome.reviewMode ?? \"full\"}`,\n `- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,\n ...(modelLabel === undefined ? [] : [`- Model: \\`${modelLabel}\\``]),\n ...(outcome.usage === undefined\n ? []\n : [\n `- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out${\n outcome.usageScope === \"coordinator\" ? \" (coordinator)\" : \"\"\n }`,\n ]),\n ...(outcome.published === undefined\n ? [\"- Dry run: nothing posted\"]\n : [`- Posted: ${outcome.published.url}`]),\n];\n\n/** The result of one action invocation: a typed skip or a settled review. */\nexport type ReviewActionResult =\n | { readonly _tag: \"Skipped\"; readonly reason: string }\n | { readonly _tag: \"Completed\"; readonly outcome: ReviewRunOutcome };\n\nconst skip = (reason: string) =>\n Effect.gen(function* () {\n yield* Console.log(`Skipping review: ${reason}`);\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", reason],\n ]);\n yield* writeStepSummary([\"### Pull-request review skipped\", `- Reason: ${reason}`]);\n return { _tag: \"Skipped\", reason } satisfies ReviewActionResult;\n });\n\n/** The Actions run URL for the review footer, or undefined outside Actions. */\nconst resolveRunUrl = Effect.fn(\"resolveRunUrl\")(function* () {\n const runId = yield* Config.string(\"GITHUB_RUN_ID\").pipe(Config.withDefault(\"\"));\n const repository = yield* Config.string(\"GITHUB_REPOSITORY\").pipe(Config.withDefault(\"\"));\n if (runId === \"\" || repository === \"\") return undefined;\n const server = yield* Config.string(\"GITHUB_SERVER_URL\").pipe(\n Config.withDefault(\"https://github.com\"),\n );\n return `${server}/${repository}/actions/runs/${runId}`;\n});\n\n/** The reviewer surface the action harness drives. `PrReview.make` and\n * `PrReview.makeFanOut` provide the state-selection effects; the legacy\n * fingerprint field remains for source compatibility with custom harnesses. */\nexport interface HarnessedReviewer<E, R, FingerprintE, FingerprintR> {\n readonly run: (runOptions?: RunReviewOptions) => Effect.Effect<ReviewRunOutcome, E, R>;\n readonly fingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;\n readonly profileFingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;\n readonly snapshot?:\n | Effect.Effect<\n {\n readonly metadata: import(\"./internal/source.ts\").PullRequestMetadata;\n readonly files: ReadonlyArray<import(\"./internal/diff.ts\").ChangedFile>;\n },\n FingerprintE,\n FingerprintR\n >\n | undefined;\n readonly filterFiles?:\n | ((\n files: ReadonlyArray<import(\"./internal/diff.ts\").ChangedFile>,\n ) => ReadonlyArray<import(\"./internal/diff.ts\").ChangedFile>)\n | undefined;\n}\n\nconst blockingReasons = (input: {\n readonly findings: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n readonly concerns: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n}): ReadonlyArray<string> => [\n ...input.findings\n .filter((finding) => finding.severity === \"blocking\")\n .map((finding) => `blocking finding: ${finding.title}`),\n ...input.concerns\n .filter((concern) => concern.severity === \"blocking\")\n .map((concern) => `blocking concern: ${concern.title}`),\n];\n\n/** Host-derived check conclusion; model verdict prose cannot weaken it. */\nexport const concludeReviewOutcome = (\n outcome: ReviewRunOutcome,\n): {\n readonly conclusion: ReviewCheckConclusion;\n readonly reasons: ReadonlyArray<string>;\n} => {\n if (outcome.coverage.status === \"incomplete\") {\n return { conclusion: \"incomplete\", reasons: outcome.coverage.reasons };\n }\n const reasons = blockingReasons({\n findings: outcome.activeFindings,\n concerns: outcome.activeConcerns,\n });\n return reasons.length > 0\n ? { conclusion: \"blocking\", reasons }\n : { conclusion: \"success\", reasons };\n};\n\nconst concludeReviewState = (state: ReviewState) => {\n const reasons = blockingReasons({\n findings: state.unresolvedFindings,\n concerns: state.unresolvedConcerns,\n });\n return reasons.length > 0\n ? ({ conclusion: \"blocking\", reasons } as const)\n : ({ conclusion: \"success\", reasons: [] } as const);\n};\n\nconst skipCoveredReview = Effect.fn(\"skipCoveredReview\")(function* (input: {\n readonly repository: string;\n readonly pullRequestNumber: number;\n readonly reason: string;\n readonly state: ReviewState;\n readonly fingerprint?: string | undefined;\n}) {\n const result = concludeReviewState(input.state);\n yield* Console.log(\n `Skipping review of ${input.repository}#${input.pullRequestNumber}: ${input.reason}.`,\n );\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", input.reason],\n ...(input.fingerprint === undefined ? [] : ([[\"fingerprint\", input.fingerprint]] as const)),\n [\"conclusion\", result.conclusion],\n [\"coverage\", \"complete\"],\n [\"review-mode\", \"incremental\"],\n ]);\n yield* writeStepSummary([\n \"### Pull-request review skipped\",\n `- Reason: ${input.reason}`,\n `- Preserved check conclusion: **${result.conclusion}**`,\n ]);\n if (result.conclusion === \"blocking\") {\n return yield* ReviewGateFailed.make({\n conclusion: \"blocking\",\n reasons: result.reasons,\n });\n }\n return { _tag: \"Skipped\", reason: input.reason } satisfies ReviewActionResult;\n});\n\n/**\n * Harness one already-built reviewer inside the Actions environment: resolve\n * the target from the event, provide the GitHub source/publisher/prior\n * reviews, validate/select bounded continuity scope, write step outputs, and apply\n * the host-derived coverage/blocker gate. Draft and non-PR skips are values;\n * an unchanged reviewed head preserves and enforces its stored conclusion. The reviewer's\n * remaining requirements — its model client, any extra tool handlers — stay\n * visible in `R` for the caller.\n */\nexport const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never>(\n reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>,\n options: {\n readonly post?: boolean | undefined;\n readonly failOn?: FailOnPolicy | undefined;\n /** Skip model execution when the current head already has complete stored coverage. */\n readonly skipUnchanged?: boolean | undefined;\n /** Incremental by default; `final` deliberately re-reviews the full PR diff. */\n readonly reviewMode?: ReviewMode | undefined;\n /** Model binding descriptor for the Actions step summary. */\n readonly modelLabel?: string | undefined;\n /** Explicit test/custom-host history override; GitHub owns the default adapter. */\n readonly priorReviews?: PriorReviews[\"Service\"] | undefined;\n /** Retire marker-bearing prior reviews after a successful post (default true). */\n readonly retireStaleReviews?: boolean | undefined;\n /**\n * Maintain one sticky \"review in progress\" issue comment, updated in\n * place when the run settles. Default false here for custom-harness\n * compatibility; the packaged action enables it by default. Dry runs\n * (`post: false`) never post progress.\n */\n readonly progressComment?: boolean | undefined;\n } = {},\n) =>\n Effect.gen(function* () {\n const event = yield* readGitHubEvent();\n if (Option.isSome(event)) {\n if (event.value.pull_request === undefined) {\n return yield* skip(\"the triggering event carries no pull request\");\n }\n if (event.value.pull_request.draft === true) {\n return yield* skip(\"the pull request is a draft\");\n }\n }\n const target = yield* resolveReviewTarget({});\n // One layer build for state selection AND the run, so both observe\n // the same cached pull-request snapshot.\n return yield* Effect.gen(function* () {\n let selection: ReturnType<typeof selectReviewRange> | undefined;\n if (reviewer.profileFingerprint !== undefined) {\n const source = yield* PullRequestSource;\n const [snapshot, profileFingerprint, currentFingerprint] = yield* Effect.all([\n reviewer.snapshot ?? Effect.all({ metadata: source.metadata, files: source.anchorFiles }),\n reviewer.profileFingerprint,\n reviewer.fingerprint === undefined\n ? Effect.succeed(undefined)\n : reviewer.fingerprint.pipe(\n Effect.map((fingerprint): string | undefined => fingerprint),\n Effect.orElseSucceed(() => undefined),\n ),\n ]);\n const { metadata, files: fullFiles } = snapshot;\n const history = options.priorReviews ?? (yield* PriorReviews);\n const stateAuthenticator = yield* ReviewStateAuthenticator;\n const recovered =\n stateAuthenticator.status === \"unavailable\"\n ? {\n state: undefined,\n failure:\n stateAuthenticator.unavailableReason ??\n \"an authenticated review-state secret is not configured\",\n }\n : yield* history.latestState.pipe(\n Effect.match({\n onFailure: (failure) => ({ state: undefined, failure: failure.reason }),\n onSuccess: (state) => ({\n state: Option.getOrUndefined(state),\n failure: undefined,\n }),\n }),\n );\n const equivalentPatchState =\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n options.skipUnchanged !== false &&\n recovered.state !== undefined &&\n currentFingerprint !== undefined &&\n validateReviewState(recovered.state, metadata, profileFingerprint) === undefined &&\n recovered.state.acceptedScopeFingerprint === currentFingerprint\n ? recovered.state\n : undefined;\n if (equivalentPatchState !== undefined) {\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason:\n equivalentPatchState.reviewedHeadSha === metadata.headSha\n ? \"the current head already has complete stored review coverage\"\n : \"the effective pull-request patch is unchanged since the last complete review\",\n state: equivalentPatchState,\n fingerprint: currentFingerprint,\n });\n }\n let comparison: ReviewHeadComparison | undefined;\n let baseComparison: ReviewHeadComparison | undefined;\n if (\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n recovered.state !== undefined\n ) {\n if (recovered.state.reviewedHeadSha === metadata.headSha) {\n comparison = ReviewHeadComparison.make({\n status: \"identical\",\n baseSha: metadata.headSha,\n headSha: metadata.headSha,\n mergeBaseSha: metadata.headSha,\n files: [],\n truncated: false,\n });\n } else {\n comparison = yield* history\n .compareHeads(recovered.state.reviewedHeadSha, metadata.headSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (comparison !== undefined && reviewer.filterFiles !== undefined) {\n comparison = ReviewHeadComparison.make({\n ...comparison,\n files: reviewer.filterFiles(comparison.files),\n });\n }\n }\n if (metadata.baseSha !== undefined && recovered.state.baseSha !== metadata.baseSha) {\n baseComparison = yield* history\n .compareHeads(recovered.state.baseSha, metadata.baseSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (baseComparison !== undefined && reviewer.filterFiles !== undefined) {\n baseComparison = ReviewHeadComparison.make({\n ...baseComparison,\n files: reviewer.filterFiles(baseComparison.files),\n });\n }\n }\n }\n selection = {\n ...selectReviewRange({\n requestedMode: options.reviewMode ?? \"incremental\",\n current: metadata,\n fullFiles,\n profileFingerprint,\n priorState: recovered.state,\n comparison,\n baseComparison,\n lookupFailure: recovered.failure,\n }),\n stateAuthenticator,\n };\n if (\n options.skipUnchanged !== false &&\n selection.mode === \"incremental\" &&\n selection.files.length === 0 &&\n selection.priorState !== undefined\n ) {\n const reason = \"no changed review scope since the last successfully reviewed head\";\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason,\n state: selection.priorState,\n });\n }\n } else if (options.skipUnchanged !== false && reviewer.fingerprint !== undefined) {\n // Preserve the pre-state custom harness contract explicitly. The\n // packaged reviewer always takes the authenticated state path above.\n const current = yield* reviewer.fingerprint;\n const history = options.priorReviews ?? (yield* PriorReviews);\n const latest = yield* history.latestFingerprint.pipe(\n Effect.orElseSucceed(() => Option.none<string>()),\n );\n if (Option.isSome(latest) && latest.value === current) {\n const reason = \"changeset unchanged since the last review\";\n yield* Console.log(\n `Skipping review of ${target.repository}#${target.number}: ${reason}.`,\n );\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", reason],\n [\"fingerprint\", current],\n [\"conclusion\", \"success\"],\n [\"coverage\", \"complete\"],\n ]);\n yield* writeStepSummary([\"### Pull-request review skipped\", `- Reason: ${reason}`]);\n return { _tag: \"Skipped\", reason } satisfies ReviewActionResult;\n }\n }\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} (${options.post === false ? \"dry run\" : \"posting\"})...`,\n );\n const runUrl = yield* resolveRunUrl();\n // Progress is a cosmetic, fail-open narration surface: it says a run is\n // working the moment execution starts and is overwritten in place when\n // the run settles. It never gates or delays the review itself.\n const progress =\n options.progressComment === true && options.post !== false\n ? Option.some(yield* ReviewProgressReporter)\n : Option.none<ReviewProgressReporter[\"Service\"]>();\n if (Option.isSome(progress)) {\n const source = yield* PullRequestSource;\n const headMetadata = yield* source.metadata.pipe(Effect.orElseSucceed(() => undefined));\n yield* progress.value.begin({\n headSha: headMetadata?.headSha,\n reviewMode: selection?.mode,\n reviewReason: selection?.reason,\n filesInScope: selection?.files.length,\n modelLabel: options.modelLabel,\n runUrl,\n });\n }\n const runReview = reviewer.run({ post: options.post ?? true, runUrl });\n const reviewEffect = Option.isSome(progress)\n ? runReview.pipe(\n Effect.tapCause(() =>\n progress.value.settle({\n outcome: \"failed\",\n runUrl,\n modelLabel: options.modelLabel,\n }),\n ),\n )\n : runReview;\n const outcome = yield* selection === undefined\n ? reviewEffect\n : reviewEffect.pipe(\n Effect.provide(selectedPullRequestSourceLayer(selection)),\n Effect.provideService(ReviewExecutionContext, selection),\n );\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n if (options.retireStaleReviews !== false && outcome.state !== undefined) {\n if (outcome.published.authorNodeId === null || outcome.published.submittedAt === null) {\n yield* Console.warn(\n \"Skipping stale-review retirement because GitHub did not return the posted review's actor and submission time.\",\n );\n } else {\n const report = yield* retireStaleReviews({\n currentReviewId: outcome.published.reviewId,\n currentReviewUrl: outcome.published.url,\n currentAuthorNodeId: outcome.published.authorNodeId,\n currentSubmittedAt: outcome.published.submittedAt,\n currentState: outcome.state,\n });\n yield* Console.log(\n `Review retirement: ${report.reviewsRetired} prior review(s), ` +\n `${report.findingsResolved} resolved finding(s), ` +\n `${report.commentsMinimized} minimized inline comment(s), ` +\n `${report.failures} failure(s).`,\n );\n }\n }\n }\n const check = concludeReviewOutcome(outcome);\n if (Option.isSome(progress)) {\n yield* progress.value.settle({\n outcome: \"reviewed\",\n conclusion: check.conclusion,\n verdict: outcome.review.verdict,\n inlineComments: outcome.plan.comments.length,\n reviewUrl: outcome.published?.url,\n runUrl,\n modelLabel: options.modelLabel,\n });\n }\n yield* writeActionOutputs(outcomeOutputs(outcome, check.conclusion));\n yield* writeStepSummary(outcomeSummary(outcome, options.modelLabel, check.conclusion));\n if (check.conclusion !== \"success\") {\n return yield* ReviewGateFailed.make({\n conclusion: check.conclusion,\n reasons: check.reasons,\n });\n }\n return { _tag: \"Completed\", outcome } satisfies ReviewActionResult;\n }).pipe(Effect.provide(gitHubReviewLayers(target)));\n });\n\n/**\n * The packaged, environment-driven action program: inputs from PR_REVIEW_*,\n * reviewer built from the packaged factory, provider client from the\n * matching credential environment variable.\n */\nexport const reviewActionProgram = Effect.gen(function* () {\n const inputs = yield* resolveActionInputs();\n const stateSecret = Option.map(\n yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_STATE_SECRET\")),\n Redacted.make,\n );\n const stateAuthenticatorLayer = Option.match(stateSecret, {\n onNone: () =>\n unavailableReviewStateAuthenticatorLayer(\n \"an authenticated review-state secret is not configured\",\n ),\n onSome: webCryptoReviewStateAuthenticatorLayer,\n });\n const guidance = yield* resolveGuidance(inputs);\n const modelLabel = describeReviewModel(inputs.provider, inputs.model, inputs.effort);\n const defaults = inputs.fanOut ? fanOutReviewBudgetLimits : reviewBudgetLimits;\n const budget =\n inputs.maxDurationMinutes === undefined\n ? defaults\n : UsageBudgetLimits.make({\n ...defaults,\n maxDurationMillis: inputs.maxDurationMinutes * 60_000,\n });\n const shared = {\n guidance,\n ignore: inputs.ignore,\n maxFindings: inputs.maxFindings,\n applyVerdict: inputs.applyVerdict,\n modelLabel,\n budget,\n };\n const harness = {\n post: inputs.post,\n failOn: inputs.failOn,\n skipUnchanged: inputs.skipUnchanged,\n reviewMode: inputs.reviewMode,\n retireStaleReviews: inputs.retireStaleReviews,\n progressComment: inputs.progressComment,\n modelLabel,\n };\n if (inputs.provider === \"anthropic\") {\n const model = makeAnthropicReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, anthropicClientLayer)),\n );\n }\n const model = makeOpenAiReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, openAiClientLayer)),\n );\n});\n\n/** Run the packaged action program on Node; the bundled action entrypoint. */\nexport const main = (): void =>\n NodeRuntime.runMain(\n reviewActionProgram.pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(\n Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer, compactReviewLoggingLayer),\n ),\n ),\n { disableErrorReporting: true },\n );\n\n/** The packaged GitHub Actions surface. */\nexport const PrReviewAction = {\n inputs: resolveActionInputs,\n run: runReviewAction,\n program: reviewActionProgram,\n main,\n} as const;\n"],"mappings":"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"}
1
+ {"version":3,"file":"action.mjs","names":[],"sources":["../src/action.ts"],"sourcesContent":["import { NodeRuntime, NodeServices } from \"@effect/platform-node\";\nimport { Config, Console, Effect, FileSystem, Layer, Option, Redacted, Schema } from \"effect\";\nimport { BudgetExceeded, UsageBudgetLimits } from \"effect-agent\";\nimport { FetchHttpClient } from \"effect/unstable/http\";\n\nimport type { ChangedFile } from \"./internal/diff.ts\";\nimport { InvalidEffortInput, parseEffortPosition, type EffortPosition } from \"./internal/effort.ts\";\nimport { PrReview, type RunReviewOptions } from \"./internal/factory.ts\";\nimport { readGitHubEvent, resolveReviewTarget, gitHubReviewLayers } from \"./internal/github-env.ts\";\nimport { PriorReviews } from \"./internal/github.ts\";\nimport { compactReviewLoggingLayer } from \"./internal/logging.ts\";\nimport { ReviewProgressReporter } from \"./internal/progress.ts\";\nimport {\n anthropicClientLayer,\n DEFAULT_PROVIDER,\n describeReviewModel,\n makeAnthropicReviewModel,\n makeOpenAiReviewModel,\n openAiClientLayer,\n type ReviewProvider,\n} from \"./internal/providers.ts\";\nimport { retireStaleReviews } from \"./internal/retirement.ts\";\nimport {\n ReviewExecutionContext,\n ReviewHeadComparison,\n ReviewStateAuthenticator,\n type ReviewMode,\n type ReviewState,\n selectReviewRange,\n selectedPullRequestSourceLayer,\n unavailableReviewStateAuthenticatorLayer,\n validateReviewState,\n webCryptoReviewStateAuthenticatorLayer,\n} from \"./internal/review-state.ts\";\nimport { fanOutReviewBudgetLimits, reviewBudgetLimits } from \"./internal/run.ts\";\nimport type { ReviewRunOutcome } from \"./internal/run.ts\";\nimport {\n normalizeRepoRelativePath,\n PullRequestSource,\n type PullRequestMetadata,\n} from \"./internal/source.ts\";\n\n// ---------------------------------------------------------------------------\n// The GitHub Actions entrypoint (deployment class E: one bounded ephemeral\n// run, no exactly-once posting). Bounded continuity state travels in GitHub\n// review bodies and is validated before reuse. Inputs arrive as\n// PR_REVIEW_* environment variables set by the action manifest; the target\n// pull request comes from the standard Actions event environment. A non-PR\n// or draft event is a typed skip — green job, nothing posted. Publication\n// happens only after the run settles, so a failed or truncated run posts\n// nothing.\n// ---------------------------------------------------------------------------\n\n/** Deprecated compatibility input; host-derived conclusions are unconditional. */\nexport const FailOnPolicy = Schema.Literals([\"never\", \"request-changes\"]);\nexport type FailOnPolicy = typeof FailOnPolicy.Type;\n\nexport const ReviewCheckConclusion = Schema.Literals([\"success\", \"blocking\", \"incomplete\"]);\nexport type ReviewCheckConclusion = typeof ReviewCheckConclusion.Type;\n\n/** The host-derived blocking or incomplete conclusion failed the check. */\nexport class ReviewGateFailed extends Schema.TaggedError<ReviewGateFailed>()(\"ReviewGateFailed\", {\n conclusion: Schema.Literals([\"blocking\", \"incomplete\"]),\n reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1_000))).check(\n Schema.isMinLength(1),\n ),\n}) {\n override get message() {\n return `Review check concluded '${this.conclusion}': ${this.reasons.join(\"; \")}`;\n }\n}\n\n/** A configured max-duration that cannot bound a run; configuration faults fail loudly. */\nexport class InvalidMaxDurationInput extends Schema.TaggedError<InvalidMaxDurationInput>()(\n \"InvalidMaxDurationInput\",\n {\n minutes: Schema.Int,\n },\n) {\n override get message() {\n return `Invalid max-duration-minutes '${this.minutes}': expected a positive number of minutes.`;\n }\n}\n\n/** Everything the packaged action reads from its environment. */\nexport interface ResolvedActionInputs {\n readonly provider: ReviewProvider;\n readonly model: string | undefined;\n readonly effort: EffortPosition | undefined;\n readonly post: boolean;\n readonly applyVerdict: boolean;\n readonly fanOut: boolean;\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n readonly ignore: ReadonlyArray<string>;\n readonly maxFindings: number | undefined;\n readonly maxDurationMinutes: number | undefined;\n readonly reviewMode: ReviewMode;\n /** Deprecated compatibility input; conclusions are always conservative. */\n readonly failOn: FailOnPolicy;\n readonly skipUnchanged: boolean;\n readonly retireStaleReviews: boolean;\n readonly progressComment: boolean;\n}\n\n/** Read the PR_REVIEW_* input surface (all optional, all defaulted). */\nexport const resolveActionInputs = Effect.fn(\"resolveActionInputs\")(function* () {\n const provider = yield* Config.literals([\"openai\", \"anthropic\"], \"PR_REVIEW_PROVIDER\").pipe(\n Config.withDefault<ReviewProvider>(DEFAULT_PROVIDER),\n );\n const model = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_MODEL\"));\n const effortRaw = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_EFFORT\"));\n let effort: EffortPosition | undefined;\n if (Option.isSome(effortRaw)) {\n effort = parseEffortPosition(effortRaw.value);\n if (effort === undefined) {\n return yield* InvalidEffortInput.make({ input: effortRaw.value });\n }\n }\n const maxDurationMinutes = Option.getOrUndefined(\n yield* Config.option(Config.int(\"PR_REVIEW_MAX_DURATION_MINUTES\")),\n );\n if (maxDurationMinutes !== undefined && maxDurationMinutes <= 0) {\n return yield* InvalidMaxDurationInput.make({ minutes: maxDurationMinutes });\n }\n const post = yield* Config.boolean(\"PR_REVIEW_POST\").pipe(Config.withDefault(true));\n const applyVerdict = yield* Config.boolean(\"PR_REVIEW_APPLY_VERDICT\").pipe(\n Config.withDefault(false),\n );\n const fanOut = yield* Config.boolean(\"PR_REVIEW_FAN_OUT\").pipe(Config.withDefault(true));\n const guidance = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE\"));\n const guidanceFile = yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_GUIDANCE_FILE\"));\n const ignoreRaw = yield* Config.string(\"PR_REVIEW_IGNORE\").pipe(Config.withDefault(\"\"));\n const maxFindings = yield* Config.option(Config.int(\"PR_REVIEW_MAX_FINDINGS\"));\n const reviewMode = yield* Config.literals([\"incremental\", \"final\"], \"PR_REVIEW_MODE\").pipe(\n Config.withDefault<ReviewMode>(\"incremental\"),\n );\n const failOn = yield* Config.literals([\"never\", \"request-changes\"], \"PR_REVIEW_FAIL_ON\").pipe(\n Config.withDefault<FailOnPolicy>(\"never\"),\n );\n const skipUnchanged = yield* Config.boolean(\"PR_REVIEW_SKIP_UNCHANGED\").pipe(\n Config.withDefault(true),\n );\n const retireStaleReviews = yield* Config.boolean(\"PR_REVIEW_RETIRE_STALE_REVIEWS\").pipe(\n Config.withDefault(true),\n );\n const progressComment = yield* Config.boolean(\"PR_REVIEW_PROGRESS_COMMENT\").pipe(\n Config.withDefault(true),\n );\n return {\n provider,\n model: Option.getOrUndefined(model),\n effort,\n post,\n applyVerdict,\n fanOut,\n guidance: Option.getOrUndefined(guidance),\n guidanceFile: Option.getOrUndefined(guidanceFile),\n ignore: ignoreRaw\n .split(\",\")\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0),\n maxFindings: Option.getOrUndefined(maxFindings),\n maxDurationMinutes,\n reviewMode,\n failOn,\n skipUnchanged,\n retireStaleReviews,\n progressComment,\n } satisfies ResolvedActionInputs;\n});\n\n/** A guidance file larger than this is refused, never silently truncated. */\nexport const MAX_GUIDANCE_FILE_CHARS = 20_000;\n\n/** A configured guidance file could not be used; configuration faults fail loudly. */\nexport class GuidanceFileUnreadable extends Schema.TaggedError<GuidanceFileUnreadable>()(\n \"GuidanceFileUnreadable\",\n {\n path: Schema.String,\n reason: Schema.String,\n },\n) {\n override get message() {\n return `Cannot use guidance file '${this.path}': ${this.reason}`;\n }\n}\n\n/**\n * Resolve the effective review guidance: the committed guidance file (a\n * repository-owned review profile) first, with any inline guidance appended.\n * A configured-but-unreadable file fails typed — a review silently running\n * without its profile would be worse than a red job.\n */\nexport const resolveGuidance = Effect.fn(\"resolveGuidance\")(function* (inputs: {\n readonly guidance: string | undefined;\n readonly guidanceFile: string | undefined;\n}) {\n const filePath = inputs.guidanceFile;\n if (filePath === undefined) return inputs.guidance;\n // The profile path is operator configuration, but it stays fail-closed\n // like every other path in this package: workspace-relative only, and the\n // size bound is checked before the file is read into memory.\n const relative = yield* normalizeRepoRelativePath(filePath).pipe(\n Effect.mapError((violation) =>\n GuidanceFileUnreadable.make({ path: filePath, reason: violation.reason }),\n ),\n );\n const fs = yield* FileSystem.FileSystem;\n const unreadable = (error: { readonly _tag: string; readonly message: string }) =>\n GuidanceFileUnreadable.make({\n path: relative,\n reason: `${error._tag}: ${error.message}`.slice(0, 2_048),\n });\n const stat = yield* fs.stat(relative).pipe(Effect.mapError(unreadable));\n const oversized = GuidanceFileUnreadable.make({\n path: relative,\n reason: `File is larger than the ${MAX_GUIDANCE_FILE_CHARS}-character guidance bound.`,\n });\n if (stat.size > BigInt(MAX_GUIDANCE_FILE_CHARS) * 4n) {\n return yield* oversized;\n }\n const content = yield* fs.readFileString(relative).pipe(Effect.mapError(unreadable));\n if (content.length > MAX_GUIDANCE_FILE_CHARS) {\n return yield* oversized;\n }\n const combined = [content.trim(), inputs.guidance ?? \"\"]\n .filter((part) => part.length > 0)\n .join(\"\\n\");\n return combined.length > 0 ? combined : undefined;\n});\n\n/** One step-output line; values must be single-line by construction. */\nconst outputLine = (name: string, value: string): string =>\n `${name}=${value.replaceAll(\"\\n\", \" \").slice(0, 1_000)}\\n`;\n\n/** Append step outputs to GITHUB_OUTPUT when present (no-op locally). */\nexport const writeActionOutputs = Effect.fn(\"writeActionOutputs\")(function* (\n entries: ReadonlyArray<readonly [name: string, value: string]>,\n) {\n const outputPath = yield* Config.string(\"GITHUB_OUTPUT\").pipe(Config.withDefault(\"\"));\n if (outputPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(\n outputPath,\n entries.map(([name, value]) => outputLine(name, value)).join(\"\"),\n {\n flag: \"a\",\n },\n );\n});\n\n/** Append markdown to the GITHUB_STEP_SUMMARY report when present (no-op locally). */\nexport const writeStepSummary = Effect.fn(\"writeStepSummary\")(function* (\n lines: ReadonlyArray<string>,\n) {\n const summaryPath = yield* Config.string(\"GITHUB_STEP_SUMMARY\").pipe(Config.withDefault(\"\"));\n if (summaryPath === \"\") return;\n const fs = yield* FileSystem.FileSystem;\n yield* fs.writeFileString(summaryPath, `${lines.join(\"\\n\")}\\n`, { flag: \"a\" });\n});\n\nconst outcomeOutputs = (\n outcome: ReviewRunOutcome,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<readonly [string, string]> => [\n [\"skipped\", \"false\"],\n [\"conclusion\", conclusion],\n [\"verdict\", outcome.review.verdict],\n [\"input-coverage\", outcome.inputCoverage.status],\n [\"review-assurance\", outcome.assurance.status],\n // Compatibility output: the precise outputs above own new integrations.\n [\"coverage\", outcome.coverage.status],\n [\"review-mode\", outcome.reviewMode ?? \"full\"],\n [\"review-reason\", outcome.reviewReason ?? \"direct full review\"],\n [\"inline-comments\", String(outcome.plan.comments.length)],\n [\"demoted-findings\", String(outcome.plan.demoted.length)],\n [\"concerns\", String(outcome.review.concerns?.length ?? 0)],\n ...(outcome.usage === undefined\n ? []\n : ([\n [\"input-tokens\", String(outcome.usage.inputTokens)],\n [\"output-tokens\", String(outcome.usage.outputTokens)],\n ] as const)),\n [\"review-url\", outcome.published?.url ?? \"\"],\n];\n\nconst outcomeSummary = (\n outcome: ReviewRunOutcome,\n modelLabel: string | undefined,\n conclusion: ReviewCheckConclusion,\n): ReadonlyArray<string> => [\n \"### Pull-request review\",\n `- Check conclusion: **${conclusion}**`,\n `- Verdict: **${outcome.review.verdict}**`,\n `- Input coverage: **${outcome.inputCoverage.status}** · scope: ${outcome.reviewMode ?? \"full\"}`,\n `- 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}`,\n `- Inline comments: ${outcome.plan.comments.length} · demoted findings: ${outcome.plan.demoted.length} · concerns: ${outcome.review.concerns?.length ?? 0}`,\n ...(modelLabel === undefined ? [] : [`- Model: \\`${modelLabel}\\``]),\n ...(outcome.usage === undefined\n ? []\n : [\n `- Tokens: ${outcome.usage.inputTokens} in / ${outcome.usage.outputTokens} out${\n outcome.usageScope === \"coordinator\" ? \" (coordinator)\" : \"\"\n }`,\n ]),\n ...(outcome.published === undefined\n ? [\"- Dry run: nothing posted\"]\n : [`- Posted: ${outcome.published.url}`]),\n];\n\n/** The result of one action invocation: a typed skip or a settled review. */\nexport type ReviewActionResult =\n | { readonly _tag: \"Skipped\"; readonly reason: string }\n | { readonly _tag: \"Completed\"; readonly outcome: ReviewRunOutcome };\n\nconst skip = (reason: string) =>\n Effect.gen(function* () {\n yield* Console.log(`Skipping review: ${reason}`);\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", reason],\n ]);\n yield* writeStepSummary([\"### Pull-request review skipped\", `- Reason: ${reason}`]);\n return { _tag: \"Skipped\", reason } satisfies ReviewActionResult;\n });\n\n/** The Actions run URL for the review footer, or undefined outside Actions. */\nconst resolveRunUrl = Effect.fn(\"resolveRunUrl\")(function* () {\n const runId = yield* Config.string(\"GITHUB_RUN_ID\").pipe(Config.withDefault(\"\"));\n const repository = yield* Config.string(\"GITHUB_REPOSITORY\").pipe(Config.withDefault(\"\"));\n if (runId === \"\" || repository === \"\") return undefined;\n const server = yield* Config.string(\"GITHUB_SERVER_URL\").pipe(\n Config.withDefault(\"https://github.com\"),\n );\n return `${server}/${repository}/actions/runs/${runId}`;\n});\n\n/**\n * The reviewer surface the action harness drives. `PrReview.make` and\n * `PrReview.makeFanOut` provide the state-selection effects. A fingerprint is\n * skip authority only when a profile fingerprint and authenticated review\n * state bind it to settled assurance; a fingerprint-only custom harness\n * remains source-compatible but deliberately runs instead of claiming green\n * assurance from unauthenticated prior-review text.\n */\nexport interface HarnessedReviewer<E, R, FingerprintE, FingerprintR> {\n readonly run: (runOptions?: RunReviewOptions) => Effect.Effect<ReviewRunOutcome, E, R>;\n /** Current effective changeset fingerprint; not standalone skip authority. */\n readonly fingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;\n readonly profileFingerprint?: Effect.Effect<string, FingerprintE, FingerprintR> | undefined;\n readonly snapshot?:\n | Effect.Effect<\n {\n readonly metadata: PullRequestMetadata;\n readonly files: ReadonlyArray<ChangedFile>;\n },\n FingerprintE,\n FingerprintR\n >\n | undefined;\n readonly filterFiles?:\n | ((files: ReadonlyArray<ChangedFile>) => ReadonlyArray<ChangedFile>)\n | undefined;\n}\n\nconst blockingReasons = (input: {\n readonly findings: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n readonly concerns: ReadonlyArray<{ readonly severity: string; readonly title: string }>;\n}): ReadonlyArray<string> => [\n ...input.findings\n .filter((finding) => finding.severity === \"blocking\")\n .map((finding) => `blocking finding: ${finding.title}`),\n ...input.concerns\n .filter((concern) => concern.severity === \"blocking\")\n .map((concern) => `blocking concern: ${concern.title}`),\n];\n\n/** Host-derived check conclusion; model verdict prose cannot weaken it. */\nexport const concludeReviewOutcome = (\n outcome: ReviewRunOutcome,\n): {\n readonly conclusion: ReviewCheckConclusion;\n readonly reasons: ReadonlyArray<string>;\n} => {\n if (outcome.inputCoverage.status === \"incomplete\") {\n return { conclusion: \"incomplete\", reasons: outcome.inputCoverage.reasons };\n }\n if (outcome.assurance.status !== \"settled\") {\n return {\n conclusion: \"incomplete\",\n reasons:\n outcome.assurance.reasons.length > 0\n ? outcome.assurance.reasons\n : [\"configured discovery and verification work did not settle\"],\n };\n }\n const reasons = blockingReasons({\n findings: outcome.activeFindings,\n concerns: outcome.activeConcerns,\n });\n return reasons.length > 0\n ? { conclusion: \"blocking\", reasons }\n : { conclusion: \"success\", reasons };\n};\n\nconst concludeReviewState = (state: ReviewState) => {\n const reasons = blockingReasons({\n findings: state.unresolvedFindings,\n concerns: state.unresolvedConcerns,\n });\n return reasons.length > 0\n ? ({ conclusion: \"blocking\", reasons } as const)\n : ({ conclusion: \"success\", reasons: [] } as const);\n};\n\nconst skipCoveredReview = Effect.fn(\"skipCoveredReview\")(function* (input: {\n readonly repository: string;\n readonly pullRequestNumber: number;\n readonly reason: string;\n readonly state: ReviewState;\n readonly fingerprint?: string | undefined;\n}) {\n const result = concludeReviewState(input.state);\n yield* Console.log(\n `Skipping review of ${input.repository}#${input.pullRequestNumber}: ${input.reason}.`,\n );\n yield* writeActionOutputs([\n [\"skipped\", \"true\"],\n [\"skip-reason\", input.reason],\n ...(input.fingerprint === undefined ? [] : ([[\"fingerprint\", input.fingerprint]] as const)),\n [\"conclusion\", result.conclusion],\n [\"input-coverage\", \"complete\"],\n [\"review-assurance\", \"settled\"],\n [\"coverage\", \"complete\"],\n [\"review-mode\", \"incremental\"],\n ]);\n yield* writeStepSummary([\n \"### Pull-request review skipped\",\n `- Reason: ${input.reason}`,\n `- Preserved check conclusion: **${result.conclusion}**`,\n ]);\n if (result.conclusion === \"blocking\") {\n return yield* ReviewGateFailed.make({\n conclusion: \"blocking\",\n reasons: result.reasons,\n });\n }\n return { _tag: \"Skipped\", reason: input.reason } satisfies ReviewActionResult;\n});\n\n/**\n * Harness one already-built reviewer inside the Actions environment: resolve\n * the target from the event, provide the GitHub source/publisher/prior\n * reviews, validate/select bounded continuity scope, write step outputs, and apply\n * the host-derived coverage/blocker gate. Draft and non-PR skips are values;\n * an unchanged reviewed head preserves and enforces its stored conclusion. The reviewer's\n * remaining requirements — its model client, any extra tool handlers — stay\n * visible in `R` for the caller.\n */\nexport const runReviewAction = <E, R, FingerprintE = never, FingerprintR = never>(\n reviewer: HarnessedReviewer<E, R, FingerprintE, FingerprintR>,\n options: {\n readonly post?: boolean | undefined;\n readonly failOn?: FailOnPolicy | undefined;\n /** Skip model execution when the current head already has complete stored coverage. */\n readonly skipUnchanged?: boolean | undefined;\n /** Incremental by default; `final` deliberately re-reviews the full PR diff. */\n readonly reviewMode?: ReviewMode | undefined;\n /** Model binding descriptor for the Actions step summary. */\n readonly modelLabel?: string | undefined;\n /** Explicit test/custom-host history override; GitHub owns the default adapter. */\n readonly priorReviews?: PriorReviews[\"Service\"] | undefined;\n /** Retire marker-bearing prior reviews after a successful post (default true). */\n readonly retireStaleReviews?: boolean | undefined;\n /**\n * Maintain one sticky \"review in progress\" issue comment, updated in\n * place when the run settles. Default false here for custom-harness\n * compatibility; the packaged action enables it by default. Dry runs\n * (`post: false`) never post progress.\n */\n readonly progressComment?: boolean | undefined;\n } = {},\n) =>\n Effect.gen(function* () {\n const event = yield* readGitHubEvent();\n if (Option.isSome(event)) {\n if (event.value.pull_request === undefined) {\n return yield* skip(\"the triggering event carries no pull request\");\n }\n if (event.value.pull_request.draft === true) {\n return yield* skip(\"the pull request is a draft\");\n }\n }\n const target = yield* resolveReviewTarget({});\n // One layer build for state selection AND the run, so both observe\n // the same cached pull-request snapshot.\n return yield* Effect.gen(function* () {\n let selection: ReturnType<typeof selectReviewRange> | undefined;\n if (reviewer.profileFingerprint !== undefined) {\n const source = yield* PullRequestSource;\n const [snapshot, profileFingerprint, currentFingerprint] = yield* Effect.all([\n reviewer.snapshot ?? Effect.all({ metadata: source.metadata, files: source.anchorFiles }),\n reviewer.profileFingerprint,\n reviewer.fingerprint === undefined\n ? Effect.succeed(undefined)\n : reviewer.fingerprint.pipe(\n Effect.map((fingerprint): string | undefined => fingerprint),\n Effect.orElseSucceed(() => undefined),\n ),\n ]);\n const { metadata, files: fullFiles } = snapshot;\n const history = options.priorReviews ?? (yield* PriorReviews);\n const stateAuthenticator = yield* ReviewStateAuthenticator;\n const recovered =\n stateAuthenticator.status === \"unavailable\"\n ? {\n state: undefined,\n failure:\n stateAuthenticator.unavailableReason ??\n \"an authenticated review-state secret is not configured\",\n }\n : yield* history.latestState.pipe(\n Effect.match({\n onFailure: (failure) => ({ state: undefined, failure: failure.reason }),\n onSuccess: (state) => ({\n state: Option.getOrUndefined(state),\n failure: undefined,\n }),\n }),\n );\n const equivalentPatchState =\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n options.skipUnchanged !== false &&\n recovered.state !== undefined &&\n currentFingerprint !== undefined &&\n validateReviewState(recovered.state, metadata, profileFingerprint) === undefined &&\n recovered.state.acceptedScopeFingerprint === currentFingerprint\n ? recovered.state\n : undefined;\n if (equivalentPatchState !== undefined) {\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason:\n equivalentPatchState.reviewedHeadSha === metadata.headSha\n ? \"the current head already has settled stored review assurance\"\n : \"the effective pull-request patch is unchanged since the last settled review\",\n state: equivalentPatchState,\n fingerprint: currentFingerprint,\n });\n }\n let comparison: ReviewHeadComparison | undefined;\n let baseComparison: ReviewHeadComparison | undefined;\n if (\n (options.reviewMode ?? \"incremental\") === \"incremental\" &&\n recovered.state !== undefined\n ) {\n if (recovered.state.reviewedHeadSha === metadata.headSha) {\n comparison = ReviewHeadComparison.make({\n status: \"identical\",\n baseSha: metadata.headSha,\n headSha: metadata.headSha,\n mergeBaseSha: metadata.headSha,\n files: [],\n truncated: false,\n });\n } else {\n comparison = yield* history\n .compareHeads(recovered.state.reviewedHeadSha, metadata.headSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (comparison !== undefined && reviewer.filterFiles !== undefined) {\n comparison = ReviewHeadComparison.make({\n ...comparison,\n files: reviewer.filterFiles(comparison.files),\n });\n }\n }\n if (metadata.baseSha !== undefined && recovered.state.baseSha !== metadata.baseSha) {\n baseComparison = yield* history\n .compareHeads(recovered.state.baseSha, metadata.baseSha)\n .pipe(Effect.orElseSucceed(() => undefined));\n if (baseComparison !== undefined && reviewer.filterFiles !== undefined) {\n baseComparison = ReviewHeadComparison.make({\n ...baseComparison,\n files: reviewer.filterFiles(baseComparison.files),\n });\n }\n }\n }\n selection = {\n ...selectReviewRange({\n requestedMode: options.reviewMode ?? \"incremental\",\n current: metadata,\n fullFiles,\n profileFingerprint,\n priorState: recovered.state,\n comparison,\n baseComparison,\n lookupFailure: recovered.failure,\n }),\n stateAuthenticator,\n };\n if (\n options.skipUnchanged !== false &&\n selection.mode === \"incremental\" &&\n selection.files.length === 0 &&\n selection.priorState !== undefined\n ) {\n const reason = \"no changed review scope since the last settled review head\";\n return yield* skipCoveredReview({\n repository: target.repository,\n pullRequestNumber: target.number,\n reason,\n state: selection.priorState,\n });\n }\n }\n yield* Console.log(\n `Reviewing ${target.repository}#${target.number} (${options.post === false ? \"dry run\" : \"posting\"})...`,\n );\n const runUrl = yield* resolveRunUrl();\n // Progress is a cosmetic, fail-open narration surface: it says a run is\n // working the moment execution starts and is overwritten in place when\n // the run settles. It never gates or delays the review itself.\n const progress =\n options.progressComment === true && options.post !== false\n ? Option.some(yield* ReviewProgressReporter)\n : Option.none<ReviewProgressReporter[\"Service\"]>();\n if (Option.isSome(progress)) {\n const source = yield* PullRequestSource;\n const headMetadata = yield* source.metadata.pipe(Effect.orElseSucceed(() => undefined));\n yield* progress.value.begin({\n headSha: headMetadata?.headSha,\n reviewMode: selection?.mode,\n reviewReason: selection?.reason,\n filesInScope: selection?.files.length,\n modelLabel: options.modelLabel,\n runUrl,\n });\n }\n const runReview = reviewer.run({ post: options.post ?? true, runUrl });\n const reviewEffect = Option.isSome(progress)\n ? runReview.pipe(\n Effect.tapCause(() =>\n progress.value.settle({\n outcome: \"failed\",\n runUrl,\n modelLabel: options.modelLabel,\n }),\n ),\n )\n : runReview;\n const outcome = yield* selection === undefined\n ? reviewEffect\n : reviewEffect.pipe(\n Effect.provide(selectedPullRequestSourceLayer(selection)),\n Effect.provideService(ReviewExecutionContext, selection),\n );\n yield* Console.log(\n `Review finished in ${outcome.turns} turn(s): verdict ${outcome.review.verdict}, ` +\n `${outcome.plan.comments.length} inline comment(s), ${outcome.plan.demoted.length} demoted finding(s).`,\n );\n if (outcome.published !== undefined) {\n yield* Console.log(`Posted ${outcome.published.event} review: ${outcome.published.url}`);\n if (options.retireStaleReviews !== false && outcome.state !== undefined) {\n if (outcome.published.authorNodeId === null || outcome.published.submittedAt === null) {\n yield* Console.warn(\n \"Skipping stale-review retirement because GitHub did not return the posted review's actor and submission time.\",\n );\n } else {\n const report = yield* retireStaleReviews({\n currentReviewId: outcome.published.reviewId,\n currentReviewUrl: outcome.published.url,\n currentAuthorNodeId: outcome.published.authorNodeId,\n currentSubmittedAt: outcome.published.submittedAt,\n currentState: outcome.state,\n });\n yield* Console.log(\n `Review retirement: ${report.reviewsRetired} prior review(s), ` +\n `${report.findingsResolved} resolved finding(s), ` +\n `${report.commentsMinimized} minimized inline comment(s), ` +\n `${report.failures} failure(s).`,\n );\n }\n }\n }\n const check = concludeReviewOutcome(outcome);\n if (Option.isSome(progress)) {\n yield* progress.value.settle({\n outcome: \"reviewed\",\n conclusion: check.conclusion,\n verdict: outcome.review.verdict,\n inlineComments: outcome.plan.comments.length,\n reviewUrl: outcome.published?.url,\n runUrl,\n modelLabel: options.modelLabel,\n });\n }\n yield* writeActionOutputs(outcomeOutputs(outcome, check.conclusion));\n yield* writeStepSummary(outcomeSummary(outcome, options.modelLabel, check.conclusion));\n if (check.conclusion !== \"success\") {\n return yield* ReviewGateFailed.make({\n conclusion: check.conclusion,\n reasons: check.reasons,\n });\n }\n return { _tag: \"Completed\", outcome } satisfies ReviewActionResult;\n }).pipe(Effect.provide(gitHubReviewLayers(target)));\n });\n\n/**\n * The packaged, environment-driven action program: inputs from PR_REVIEW_*,\n * reviewer built from the packaged factory, provider client from the\n * matching credential environment variable.\n */\nexport const reviewActionProgram = Effect.gen(function* () {\n const inputs = yield* resolveActionInputs();\n const stateSecret = Option.map(\n yield* Config.option(Config.nonEmptyString(\"PR_REVIEW_STATE_SECRET\")),\n Redacted.make,\n );\n const stateAuthenticatorLayer = Option.match(stateSecret, {\n onNone: () =>\n unavailableReviewStateAuthenticatorLayer(\n \"an authenticated review-state secret is not configured\",\n ),\n onSome: webCryptoReviewStateAuthenticatorLayer,\n });\n const guidance = yield* resolveGuidance(inputs);\n const modelLabel = describeReviewModel(inputs.provider, inputs.model, inputs.effort);\n const defaults = inputs.fanOut ? fanOutReviewBudgetLimits : reviewBudgetLimits;\n const budget =\n inputs.maxDurationMinutes === undefined\n ? defaults\n : UsageBudgetLimits.make({\n ...defaults,\n maxDurationMillis: inputs.maxDurationMinutes * 60_000,\n });\n const shared = {\n guidance,\n ignore: inputs.ignore,\n maxFindings: inputs.maxFindings,\n applyVerdict: inputs.applyVerdict,\n modelLabel,\n budget,\n };\n const harness = {\n post: inputs.post,\n failOn: inputs.failOn,\n skipUnchanged: inputs.skipUnchanged,\n reviewMode: inputs.reviewMode,\n retireStaleReviews: inputs.retireStaleReviews,\n progressComment: inputs.progressComment,\n modelLabel,\n };\n if (inputs.provider === \"anthropic\") {\n const model = makeAnthropicReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, anthropicClientLayer)),\n );\n }\n const model = makeOpenAiReviewModel(inputs.model, inputs.effort);\n const reviewer = inputs.fanOut\n ? PrReview.makeFanOut({ ...shared, model })\n : PrReview.make({ ...shared, model });\n return yield* runReviewAction(reviewer, harness).pipe(\n Effect.provide(Layer.merge(stateAuthenticatorLayer, openAiClientLayer)),\n );\n});\n\n/** Run the packaged action program on Node; the bundled action entrypoint. */\nexport const main = (): void =>\n NodeRuntime.runMain(\n reviewActionProgram.pipe(\n Effect.tapError((error) =>\n Console.error(\n Schema.is(BudgetExceeded)(error)\n ? `Budget exceeded: ${error.limit} observed ${error.observedValue}, limit ${error.limitValue}.`\n : String(error),\n ),\n ),\n Effect.scoped,\n Effect.provide(\n Layer.mergeAll(NodeServices.layer, FetchHttpClient.layer, compactReviewLoggingLayer),\n ),\n ),\n { disableErrorReporting: true },\n );\n\n/** The packaged GitHub Actions surface. */\nexport const PrReviewAction = {\n inputs: resolveActionInputs,\n run: runReviewAction,\n program: reviewActionProgram,\n main,\n} as const;\n"],"mappings":"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"}
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { c as fingerprintUnchanged } from "./github-DnenG3be.mjs";
2
- import { C as PrReview, H as InvalidEffortInput, N as ReviewPublicationPlan, W as parseEffortPosition, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, s as makeAnthropicReviewModel, t as DEFAULT_MODEL } from "./providers-BE83_Tfo.mjs";
1
+ import { c as fingerprintUnchanged } from "./github-BgtP7Rdv.mjs";
2
+ import { C as PrReview, N as ReviewPublicationPlan, U as parseEffortPosition, V as InvalidEffortInput, a as anthropicClientLayer, c as makeOpenAiReviewModel, f as gitHubReviewLayers, l as openAiClientLayer, m as resolveReviewTarget, n as DEFAULT_PROVIDER, o as describeReviewModel, s as makeAnthropicReviewModel, t as DEFAULT_MODEL } from "./providers-BguZK4B_.mjs";
3
3
  import { Console, Effect, Layer, Option, Schema } from "effect";
4
4
  import { BudgetExceeded } from "effect-agent";
5
5
  import { FetchHttpClient } from "effect/unstable/http";