@effect-agent/pr-review 0.1.0-beta.28 → 0.1.0-beta.29
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 +9 -204
- package/dist/index.d.mts +87 -914
- package/dist/index.mjs +163 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -18
- package/src/index.ts +1 -25
- package/src/review.ts +212 -0
- package/dist/action.d.mts +0 -215
- package/dist/action.mjs +0 -505
- package/dist/action.mjs.map +0 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -106
- package/dist/cli.mjs.map +0 -1
- package/dist/fan-out-C3yG1cx3.d.mts +0 -1526
- package/dist/github-CCuLgyqb.mjs +0 -3437
- package/dist/github-CCuLgyqb.mjs.map +0 -1
- package/dist/logging-Q4j0oub-.mjs +0 -75
- package/dist/logging-Q4j0oub-.mjs.map +0 -1
- package/dist/providers-Br9FRn7j.mjs +0 -1349
- package/dist/providers-Br9FRn7j.mjs.map +0 -1
- package/dist/testing.d.mts +0 -86
- package/dist/testing.mjs +0 -184
- package/dist/testing.mjs.map +0 -1
- package/src/action.ts +0 -906
- package/src/cli.ts +0 -235
- package/src/internal/action-entry.ts +0 -45
- package/src/internal/adjudication.ts +0 -415
- package/src/internal/anchors.ts +0 -20
- package/src/internal/coverage.ts +0 -357
- package/src/internal/diff.ts +0 -193
- package/src/internal/effort.ts +0 -86
- package/src/internal/factory.ts +0 -357
- package/src/internal/fan-out-scripted.ts +0 -77
- package/src/internal/fan-out.ts +0 -1148
- package/src/internal/fingerprint.ts +0 -89
- package/src/internal/fixtures.ts +0 -148
- package/src/internal/github-env.ts +0 -164
- package/src/internal/github.ts +0 -1218
- package/src/internal/ignore.ts +0 -88
- package/src/internal/logging.ts +0 -124
- package/src/internal/profiles.ts +0 -91
- package/src/internal/progress.ts +0 -433
- package/src/internal/providers.ts +0 -133
- package/src/internal/render.ts +0 -819
- package/src/internal/retirement.ts +0 -337
- package/src/internal/review-agent.ts +0 -543
- package/src/internal/review-state.ts +0 -782
- package/src/internal/review-units.ts +0 -493
- package/src/internal/run.ts +0 -611
- package/src/internal/scripted.ts +0 -108
- package/src/internal/source.ts +0 -110
- package/src/testing.ts +0 -8
package/README.md
CHANGED
|
@@ -1,210 +1,15 @@
|
|
|
1
1
|
# @effect-agent/pr-review
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
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.
|
|
3
|
+
A small, provider-neutral review agent.
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
The package accepts an already-collected diff, makes one model call with no
|
|
6
|
+
tools, validates the returned paths and line anchors, and returns a structured
|
|
7
|
+
report with token usage. It knows nothing about GitHub, webhooks, comments,
|
|
8
|
+
providers, retries, or review history; those belong to the host channel.
|
|
9
|
+
Each finding carries a severity and problem category so a host can render a
|
|
10
|
+
scannable review without another model call.
|
|
13
11
|
|
|
14
12
|
```ts
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
PrReview,
|
|
18
|
-
describeReviewModel,
|
|
19
|
-
fullReviewExecutionContextLayer,
|
|
20
|
-
gitHubReviewLayers,
|
|
21
|
-
resolveReviewTarget,
|
|
22
|
-
makeOpenAiReviewModel,
|
|
23
|
-
openAiClientLayer,
|
|
24
|
-
} from "@effect-agent/pr-review";
|
|
25
|
-
|
|
26
|
-
const reviewer = PrReview.make({ model: makeOpenAiReviewModel() });
|
|
27
|
-
|
|
28
|
-
const program = Effect.gen(function* () {
|
|
29
|
-
const target = yield* resolveReviewTarget({ repository: "acme/api", number: 123 });
|
|
30
|
-
return yield* reviewer
|
|
31
|
-
.run({ post: true })
|
|
32
|
-
.pipe(
|
|
33
|
-
Effect.provide(
|
|
34
|
-
fullReviewExecutionContextLayer("explicit direct full review").pipe(
|
|
35
|
-
Layer.provideMerge(Layer.merge(gitHubReviewLayers(target), openAiClientLayer)),
|
|
36
|
-
),
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
});
|
|
13
|
+
const reviewer = makeReviewer({ model, guidance });
|
|
14
|
+
const outcome = yield * reviewer.review(request);
|
|
40
15
|
```
|
|
41
|
-
|
|
42
|
-
The run's requirement channel keeps every real dependency visible: the
|
|
43
|
-
`PullRequestSource`, `ReviewPublisher`, `ReviewAdjudicationHost`, and
|
|
44
|
-
`ReviewExecutionContext` ports, the provider client, and the handler Layer of
|
|
45
|
-
any extra tool you add. Direct callers provide an explicit full-review context
|
|
46
|
-
as above; the packaged action supplies its selected incremental or full range.
|
|
47
|
-
Anthropic is equally supported
|
|
48
|
-
(`makeAnthropicReviewModel`, `anthropicClientLayer`), and the factory accepts
|
|
49
|
-
any Effect AI Model.
|
|
50
|
-
|
|
51
|
-
The built-in OpenAI binding can opt each request into Fast mode:
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
const reviewer = PrReview.make({
|
|
55
|
-
model: makeOpenAiReviewModel("gpt-5.6-sol", undefined, "fast"),
|
|
56
|
-
modelLabel: describeReviewModel("openai", "gpt-5.6-sol", undefined, "fast"),
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
`fast` is the only packaged service-tier value. The helper sends it as the
|
|
61
|
-
OpenAI Responses `service_tier`; omitting it leaves the request unset so the
|
|
62
|
-
OpenAI project default applies. The Action and CLI reject a service tier when
|
|
63
|
-
the selected provider is not OpenAI. The model label includes the tier and
|
|
64
|
-
therefore changes both ordinary and profile fingerprints.
|
|
65
|
-
|
|
66
|
-
## Adapt
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
const reviewer = PrReview.make({
|
|
70
|
-
model: makeAnthropicReviewModel("claude-sonnet-5"),
|
|
71
|
-
guidance: (mission) => [
|
|
72
|
-
"This is an Effect codebase. Flag naked Promises in public APIs.",
|
|
73
|
-
mission.changedFileCount > 50 ? "Large PR: prioritize breadth over nit depth." : "",
|
|
74
|
-
],
|
|
75
|
-
ignore: ["**/*.lock", "dist/**"],
|
|
76
|
-
maxFindings: 10,
|
|
77
|
-
policy: { maxTurns: 20, maxToolCalls: 40, maxDuration: "10 minutes", toolConcurrency: 2 },
|
|
78
|
-
extraTools: [MyReadonlyTool], // must be annotated ToolExecutionClass "readonly"
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Model output is untrusted. The host revalidates anchors, enforces `maxFindings`, and publishes only
|
|
83
|
-
after the run settles. `PrReview.makeFanOut` partitions bounded evidence and schedules general and
|
|
84
|
-
specialist discovery for every unit, followed by independent candidate verification. Verifiers
|
|
85
|
-
receive the exact claims and enough neighboring evidence to reject a plausible mistake, but no
|
|
86
|
-
discovery reasoning.
|
|
87
|
-
|
|
88
|
-
Each failed pass retries once, then carries its unit forward. The host accepts only confirmed
|
|
89
|
-
candidate IDs, discards anchors outside assigned evidence, deduplicates claims in plan order, and
|
|
90
|
-
derives the verdict from validated severities. Shared `guidance` reaches discovery and verification.
|
|
91
|
-
|
|
92
|
-
When GitHub omits a textual patch, the source reads bounded, strict UTF-8 base/head content and
|
|
93
|
-
labels it with non-anchorable `B`/`H` lines. Invalid UTF-8, binary NUL content, missing sides, and
|
|
94
|
-
oversized files remain unreviewable and keep input coverage incomplete. Exclude them with ignore
|
|
95
|
-
globs only when that is intentional.
|
|
96
|
-
|
|
97
|
-
## Assurance model
|
|
98
|
-
|
|
99
|
-
The public result deliberately separates two claims:
|
|
100
|
-
|
|
101
|
-
- **Input coverage** says every reviewable required path in the selected
|
|
102
|
-
scope was assigned every deterministic bounded evidence shard and
|
|
103
|
-
separately names partially assigned and over-capacity paths, the exact
|
|
104
|
-
unassigned-shard count with a bounded identifier sample, undiffable paths,
|
|
105
|
-
and source truncation. A large textual diff is split across shards and
|
|
106
|
-
units instead of being called partial merely for exceeding one prompt
|
|
107
|
-
chunk. Input coverage does not say the model understood the evidence.
|
|
108
|
-
- **Review assurance** says every scheduled general discovery pass, required
|
|
109
|
-
independent specialist pass, and candidate-verification pass settled after
|
|
110
|
-
at most one retry. It reports discovered, confirmed, rejected, unsettled,
|
|
111
|
-
and discarded (invalid-anchor) candidate counts plus failed pass IDs. A
|
|
112
|
-
failed pass is a reviewer-side gap: its unit's paths carry forward as
|
|
113
|
-
retryable scope and the next incremental run re-reviews exactly them.
|
|
114
|
-
|
|
115
|
-
`settled` assurance means the configured work completed; it never means the
|
|
116
|
-
review is exhaustive or the pull request is defect-free. Risk classification
|
|
117
|
-
is deterministic host policy over paths and bounded text and intentionally
|
|
118
|
-
favors redundant work, but it cannot recognize every semantically risky
|
|
119
|
-
change. The specialist pass is context-independent redundancy, not a claim of
|
|
120
|
-
provider or model diversity. Running it for every unit prevents classifier
|
|
121
|
-
silence from suppressing scrutiny; the category labels still cannot prove
|
|
122
|
-
that every semantic risk was recognized. Hosts and UI use `inputCoverage` and `assurance`.
|
|
123
|
-
The flat reviewer has path-input accounting but no independent verifier, so
|
|
124
|
-
its assurance is `unverified` and the Action check cannot report success from
|
|
125
|
-
that shape.
|
|
126
|
-
|
|
127
|
-
## Maintainer adjudication
|
|
128
|
-
|
|
129
|
-
A maintainer can settle a finding without changing code, from the pull request itself:
|
|
130
|
-
|
|
131
|
-
- On a finding's inline thread, reply `/adjudicate accepted-risk|refuted|obsolete[: reason]`.
|
|
132
|
-
The thread names the target, so the verb alone suffices.
|
|
133
|
-
- For an unanchored concern, comment
|
|
134
|
-
`/adjudicate <disposition> "<exact concern title>"[: reason]` in the PR conversation. The quoted
|
|
135
|
-
title is required and must match exactly.
|
|
136
|
-
|
|
137
|
-
An adjudicated identity leaves active findings, verdict counts, and the check conclusion; it
|
|
138
|
-
renders in a collapsed "Adjudicated" section instead, and the final audit distinguishes fixed,
|
|
139
|
-
adjudicated, and still-open items. The reviewer prompt names each adjudication so the model does
|
|
140
|
-
not re-raise it without materially new evidence. Identity is exact — path, line range, and title
|
|
141
|
-
for findings, title alone for concerns — so a materially different finding at the same location is
|
|
142
|
-
untouched. Adjudications persist in the signed review state, and the skip-unchanged path re-reads
|
|
143
|
-
them, so an adjudication lifts a blocking check without a new commit.
|
|
144
|
-
|
|
145
|
-
**Authorization is fail-closed**: only comments whose `author_association` is OWNER, MEMBER, or
|
|
146
|
-
COLLABORATOR adjudicate. Anything else — third parties, bots, malformed commands — is ignored
|
|
147
|
-
(logged at debug). Later adjudications of the same identity win by comment creation order, bounded
|
|
148
|
-
at 20 stored entries (oldest dropped with a logged notice). If either GitHub listing surface fails,
|
|
149
|
-
or one thread exceeds the bounded authorized-command history, fresh collection is discarded and
|
|
150
|
-
the stored set stands unchanged.
|
|
151
|
-
|
|
152
|
-
**Rejected alternative — parsing free-text rebuttals** ("this is fine because …" replies): only an
|
|
153
|
-
explicit, authorized verb is auditable and fail-closed. Inferring intent from prose would let model
|
|
154
|
-
output or third-party comments silently suppress findings, and nobody could later say which comment
|
|
155
|
-
dismissed what.
|
|
156
|
-
|
|
157
|
-
## What a posted review looks like
|
|
158
|
-
|
|
159
|
-
The host derives callouts, statistics, walkthroughs, inline comments, and the final prompt from
|
|
160
|
-
validated findings. Model prose cannot choose the verdict or smuggle paths outside the changeset.
|
|
161
|
-
The [Action guide](../../action/README.md) documents the rendered review and workflow outputs.
|
|
162
|
-
|
|
163
|
-
## Swap a port
|
|
164
|
-
|
|
165
|
-
Tools observe the pull request only through `PullRequestSource`; publication
|
|
166
|
-
happens only through `ReviewPublisher`. Provide your own Layers to review
|
|
167
|
-
any diff-shaped input or publish elsewhere. The GitHub REST adapters are
|
|
168
|
-
one implementation, not the contract.
|
|
169
|
-
|
|
170
|
-
## Test what you adapted
|
|
171
|
-
|
|
172
|
-
```ts
|
|
173
|
-
import {
|
|
174
|
-
fixturePullRequestSourceLayer,
|
|
175
|
-
collectingReviewPublisherLayer,
|
|
176
|
-
makeOfflineReviewerModel,
|
|
177
|
-
makePromptKeyedModel,
|
|
178
|
-
} from "@effect-agent/pr-review/testing";
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Deterministic in-memory adapters for both ports plus prompt-keyed scripted
|
|
182
|
-
models that call the real Tools. Tests need no network or credentials and exercise every ordinary
|
|
183
|
-
gate.
|
|
184
|
-
|
|
185
|
-
## Action and CLI
|
|
186
|
-
|
|
187
|
-
The prebuilt GitHub Action handles signed incremental state, fail-closed scope selection, sticky
|
|
188
|
-
progress, stale-review retirement, and final audits. Failed stages remain attached to their exact
|
|
189
|
-
unchanged paths; failed verification reopens discovery only for its paths because candidates are
|
|
190
|
-
not persisted. See its [setup and behavior guide](../../action/README.md) and
|
|
191
|
-
[input reference](../../action/action.yml). Custom hosts can import `runReviewAction` from
|
|
192
|
-
`@effect-agent/pr-review/action`.
|
|
193
|
-
|
|
194
|
-
For an amended or force-pushed head, the GitHub adapter compares complete commit tree snapshots
|
|
195
|
-
across a bounded PR and continuity path set. Selection then uses the current full PR records, so
|
|
196
|
-
patches and rename metadata remain intact. An unavailable, malformed, or truncated tree falls back
|
|
197
|
-
to the full PR and records the cause in the review reason.
|
|
198
|
-
|
|
199
|
-
Run the CLI with
|
|
200
|
-
`bun src/cli.ts --repo owner/name --pr 123 [--post] [--provider anthropic] [--service-tier fast] [--fan-out]`.
|
|
201
|
-
|
|
202
|
-
## Bounds, spelled out
|
|
203
|
-
|
|
204
|
-
Every definition has a finite `AgentPolicy` and run-level `UsageBudgetLimits`. The reviewer rejects
|
|
205
|
-
file versions beyond 200k bytes or characters and patchless B/H renderings beyond 220k characters.
|
|
206
|
-
It reads at most 300 changed files. Each fan-out unit holds at most 12 files, 12 complete evidence
|
|
207
|
-
shards, and 240,000 evidence characters. At most eight units produce 24 attached children with
|
|
208
|
-
child concurrency capped at four. The result reports every capacity overflow. Any blocking
|
|
209
|
-
finding, input gap, failed pass, exhausted pass, mismatched candidate batch, or unsettled
|
|
210
|
-
verification prevents a green check.
|