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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { Context, DateTime, Effect, Encoding, Layer, Option, Redacted, Result, Schema } from "effect";
2
- import { Agent, AgentPolicy, Subagent, SubagentPolicy, SubagentRuntime, ToolExecutionClass, ToolResultBounds } from "effect-agent";
2
+ import { Agent, AgentPolicy, AgentRuntime, ToolExecutionClass, ToolResultBounds } from "effect-agent";
3
3
  import { Tool, Toolkit } from "effect/unstable/ai";
4
4
  import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
5
5
  //#region src/internal/diff.ts
@@ -579,6 +579,228 @@ const PullRequestReviewer = Agent.define("pr-reviewer", {
579
579
  }
580
580
  });
581
581
  //#endregion
582
+ //#region src/internal/coverage.ts
583
+ var FailedReviewUnit = class extends Schema.Class("@effect-agent/pr-review/FailedReviewUnit")({
584
+ unitId: Schema.NonEmptyString.check(Schema.isMaxLength(32)),
585
+ errorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256))
586
+ }) {};
587
+ /**
588
+ * Compatibility diagnostic retained for callers that consumed the original
589
+ * `coverage` field. New UI and state decisions use ReviewInputCoverage and
590
+ * ReviewAssurance directly.
591
+ */
592
+ var ReviewCoverage = class extends Schema.Class("@effect-agent/pr-review/ReviewCoverage")({
593
+ status: Schema.Literals(["complete", "incomplete"]),
594
+ requiredPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
595
+ reviewedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
596
+ unreviewedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
597
+ failedUnits: Schema.Array(FailedReviewUnit).check(Schema.isMaxLength(8)),
598
+ reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(32))
599
+ }) {};
600
+ var ReviewInputCoverage = class extends Schema.Class("@effect-agent/pr-review/ReviewInputCoverage")({
601
+ status: Schema.Literals(["complete", "incomplete"]),
602
+ requiredPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
603
+ assignedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
604
+ /** Assigned paths whose model-visible diff was truncated by the evidence bound. */
605
+ partialPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
606
+ unassignedPaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
607
+ /**
608
+ * Paths with neither a textual diff nor bounded base/head text (binaries,
609
+ * oversized files). Fail-closed: they keep the status incomplete for as
610
+ * long as they are part of the pull request — an unreviewable change must
611
+ * never authorize a green check. Exclude them deliberately with ignore
612
+ * globs when that is intended.
613
+ */
614
+ undiffablePaths: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(512))).check(Schema.isMaxLength(300)),
615
+ reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(20))
616
+ }) {};
617
+ var FailedReviewPass = class extends Schema.Class("@effect-agent/pr-review/FailedReviewPass")({
618
+ workId: Schema.NonEmptyString.check(Schema.isMaxLength(96)),
619
+ stage: Schema.Literals([
620
+ "discovery",
621
+ "specialist",
622
+ "verification"
623
+ ]),
624
+ errorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256))
625
+ }) {};
626
+ /**
627
+ * Settlement of scheduled review work. `incomplete` means reviewer-side work
628
+ * failed after its bounded retry — a machinery gap that is carried forward and
629
+ * retried on the next run, never a statement about the code under review.
630
+ * `unverified` is the flat reviewer's honest constant: one pass with no
631
+ * independent verifier is neither settled assurance nor a failure.
632
+ */
633
+ var ReviewAssurance = class extends Schema.Class("@effect-agent/pr-review/ReviewAssurance")({
634
+ status: Schema.Literals([
635
+ "settled",
636
+ "incomplete",
637
+ "unverified"
638
+ ]),
639
+ requiredGeneralDiscoveryPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
640
+ completedGeneralDiscoveryPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
641
+ requiredSpecialistPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
642
+ completedSpecialistPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
643
+ requiredVerificationPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
644
+ completedVerificationPasses: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
645
+ discoveredCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
646
+ confirmedCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
647
+ rejectedCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
648
+ unsettledCandidates: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
649
+ /** Discovery claims discarded for anchors/paths outside their assigned evidence. */
650
+ discardedInvalidFindings: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
651
+ failedPasses: Schema.Array(FailedReviewPass).check(Schema.isMaxLength(64)),
652
+ reasons: Schema.Array(Schema.NonEmptyString.check(Schema.isMaxLength(1e3))).check(Schema.isMaxLength(32))
653
+ }) {};
654
+ const toolTrace = (events) => {
655
+ const declared = /* @__PURE__ */ new Map();
656
+ const succeeded = /* @__PURE__ */ new Map();
657
+ const failed = /* @__PURE__ */ new Map();
658
+ for (const event of events) {
659
+ if (event._tag === "ToolCallDeclared") declared.set(event.toolCallId, event);
660
+ if (event._tag === "ToolCallSucceeded") succeeded.set(event.toolCallId, event);
661
+ if (event._tag === "ToolCallFailed") failed.set(event.toolCallId, event);
662
+ }
663
+ return {
664
+ declared,
665
+ succeeded,
666
+ failed
667
+ };
668
+ };
669
+ const sortedUnique = (values) => [...new Set(values)].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
670
+ /** Render a bounded, deterministic "label (n): a, b, … (+k more)" reason line. */
671
+ const boundedListReason = (label, values) => {
672
+ const items = sortedUnique(values);
673
+ let rendered = `${label} (${items.length}): `;
674
+ for (let index = 0; index < items.length; index += 1) {
675
+ const item = items[index] ?? "";
676
+ const separator = index === 0 ? "" : ", ";
677
+ const omitted = items.length - index - 1;
678
+ const suffix = omitted === 0 ? "" : ` … (+${omitted} more)`;
679
+ if (`${rendered}${separator}${item}${suffix}`.length > 1e3) {
680
+ const omission = `… (+${items.length - index} more)`;
681
+ return `${rendered.slice(0, 1e3 - omission.length)}${omission}`;
682
+ }
683
+ rendered = `${rendered}${separator}${item}`;
684
+ }
685
+ return rendered;
686
+ };
687
+ const anchorSurfaceAdjusted = (inputCoverage, anchorFiles, totalAnchorFiles) => anchorFiles.length >= totalAnchorFiles ? inputCoverage : ReviewInputCoverage.make({
688
+ ...inputCoverage,
689
+ status: "incomplete",
690
+ reasons: [...inputCoverage.reasons, `full pull-request anchor surface exposed ${anchorFiles.length} of ${totalAnchorFiles} required files`]
691
+ });
692
+ /** The flat reviewer's honest constant assurance: one pass, no verifier. */
693
+ const flatAssurance = () => ReviewAssurance.make({
694
+ status: "unverified",
695
+ requiredGeneralDiscoveryPasses: 1,
696
+ completedGeneralDiscoveryPasses: 1,
697
+ requiredSpecialistPasses: 0,
698
+ completedSpecialistPasses: 0,
699
+ requiredVerificationPasses: 0,
700
+ completedVerificationPasses: 0,
701
+ discoveredCandidates: 0,
702
+ confirmedCandidates: 0,
703
+ rejectedCandidates: 0,
704
+ unsettledCandidates: 0,
705
+ discardedInvalidFindings: 0,
706
+ failedPasses: [],
707
+ reasons: ["flat review has no independent candidate-verification pass; use the fan-out pipeline for a settled assurance result"]
708
+ });
709
+ /**
710
+ * Assess one settled flat run from its Run event trace: which required paths
711
+ * received successful bounded diff evidence. This observes tool INPUT
712
+ * assignment only — the host cannot know which evidence the model weighed.
713
+ */
714
+ const assessFlatReview = (input) => {
715
+ const trace = toolTrace(input.events);
716
+ const requiredPaths = sortedUnique(input.files.map((file) => file.path));
717
+ const assigned = /* @__PURE__ */ new Set();
718
+ const partial = /* @__PURE__ */ new Set();
719
+ const failedPaths = /* @__PURE__ */ new Set();
720
+ for (const [toolCallId, declaration] of trace.declared) {
721
+ if (declaration.toolName !== "read_file_diff") continue;
722
+ const query = Schema.decodeUnknownOption(FileDiffQuery)(declaration.parameters);
723
+ if (Option.isNone(query)) continue;
724
+ const success = trace.succeeded.get(toolCallId);
725
+ if (success !== void 0) {
726
+ assigned.add(query.value.path);
727
+ const view = Schema.decodeUnknownOption(FileDiffView)(success.result);
728
+ if (Option.isSome(view) && view.value.truncated) partial.add(query.value.path);
729
+ }
730
+ if (trace.failed.has(toolCallId)) failedPaths.add(query.value.path);
731
+ }
732
+ const undiffable = new Set(input.files.filter((file) => !isReviewableFile(file)).map((file) => file.path));
733
+ const unassigned = requiredPaths.filter((path) => !undiffable.has(path) && (!assigned.has(path) || failedPaths.has(path)));
734
+ const reasons = [];
735
+ if (input.files.length < input.totalFiles) reasons.push(`review range exposed ${input.files.length} of ${input.totalFiles} required files`);
736
+ if (undiffable.size > 0) reasons.push(boundedListReason("required paths have no reviewable diff or bounded text", undiffable));
737
+ if (failedPaths.size > 0) reasons.push(boundedListReason("diff reads failed", failedPaths));
738
+ if (partial.size > 0) reasons.push(boundedListReason("model-visible diff evidence was truncated", partial));
739
+ if (unassigned.length > 0) reasons.push(boundedListReason("required paths received no successful diff input", unassigned));
740
+ return {
741
+ inputCoverage: anchorSurfaceAdjusted(ReviewInputCoverage.make({
742
+ status: reasons.length === 0 ? "complete" : "incomplete",
743
+ requiredPaths,
744
+ assignedPaths: sortedUnique(assigned),
745
+ partialPaths: sortedUnique(partial),
746
+ unassignedPaths: sortedUnique(unassigned),
747
+ undiffablePaths: sortedUnique(undiffable),
748
+ reasons
749
+ }), input.anchorFiles, input.totalAnchorFiles),
750
+ assurance: flatAssurance(),
751
+ unreviewedPaths: sortedUnique([...unassigned, ...undiffable])
752
+ };
753
+ };
754
+ /**
755
+ * Input coverage of one host-scheduled fan-out plan: which required paths the
756
+ * bounded plan actually assigned complete evidence for. Capacity overflow and
757
+ * undiffable paths are both real gaps; the pipeline carries them so the check
758
+ * stays fail-closed until they are reviewed, removed, or explicitly ignored.
759
+ */
760
+ const fanOutInputCoverage = (input) => {
761
+ const plan = input.plan;
762
+ const assignedPaths = sortedUnique(plan.units.flatMap((unit) => unit.paths));
763
+ const unassignedPaths = sortedUnique(plan.unassignedPaths);
764
+ const reasons = [];
765
+ if (plan.truncated) reasons.push(`review range exposed ${input.files.length} of ${input.totalFiles} required files`);
766
+ if (plan.undiffablePaths.length > 0) reasons.push(boundedListReason("required paths have no reviewable diff or bounded text", plan.undiffablePaths));
767
+ if (plan.partialEvidencePaths.length > 0) reasons.push(boundedListReason("fan-out capacity left some deterministic evidence shards unassigned", plan.partialEvidencePaths));
768
+ if (plan.unassignedEvidenceShardCount > 0) {
769
+ reasons.push(`${plan.unassignedEvidenceShardCount} deterministic evidence shard(s) exceeded fan-out capacity`);
770
+ reasons.push(boundedListReason(`unassigned evidence shard identifier sample (${plan.unassignedEvidenceShardIds.length} of ${plan.unassignedEvidenceShardCount})`, plan.unassignedEvidenceShardIds));
771
+ }
772
+ if (plan.unassignedPaths.length > 0) reasons.push(boundedListReason("fan-out capacity left paths unassigned", plan.unassignedPaths));
773
+ return anchorSurfaceAdjusted(ReviewInputCoverage.make({
774
+ status: reasons.length === 0 ? "complete" : "incomplete",
775
+ requiredPaths: sortedUnique(input.files.map((file) => file.path)),
776
+ assignedPaths,
777
+ partialPaths: plan.partialEvidencePaths,
778
+ unassignedPaths,
779
+ undiffablePaths: sortedUnique(plan.undiffablePaths),
780
+ reasons
781
+ }), input.anchorFiles, input.totalAnchorFiles);
782
+ };
783
+ /** Compatibility aggregate over the two precise claims. */
784
+ const compatibilityCoverage = (inputCoverage, assurance) => {
785
+ const assuranceIncomplete = assurance.status === "incomplete";
786
+ const failedUnits = /* @__PURE__ */ new Map();
787
+ for (const pass of assurance.failedPasses) {
788
+ const unitId = pass.workId.slice(0, 8);
789
+ if (!failedUnits.has(unitId)) failedUnits.set(unitId, FailedReviewUnit.make({
790
+ unitId,
791
+ errorTag: `${pass.stage}:${pass.errorTag}`
792
+ }));
793
+ }
794
+ return ReviewCoverage.make({
795
+ status: inputCoverage.status === "complete" && !assuranceIncomplete ? "complete" : "incomplete",
796
+ requiredPaths: inputCoverage.requiredPaths,
797
+ reviewedPaths: inputCoverage.assignedPaths,
798
+ unreviewedPaths: sortedUnique([...inputCoverage.partialPaths, ...inputCoverage.unassignedPaths]),
799
+ failedUnits: [...failedUnits.values()].slice(0, 8),
800
+ reasons: [...inputCoverage.reasons, ...assuranceIncomplete ? assurance.reasons : []]
801
+ });
802
+ };
803
+ //#endregion
582
804
  //#region src/internal/review-units.ts
583
805
  /** The delegation fan-out bound: one parent Run spawns at most this many children. */
584
806
  const MAX_REVIEW_UNITS = 8;
@@ -928,6 +1150,20 @@ const rankAndDedupeFindings = (findings) => {
928
1150
  return left.startLine - right.startLine;
929
1151
  }).slice(0, 20);
930
1152
  };
1153
+ /**
1154
+ * The concern analogue of `rankAndDedupeFindings`: dedupe by exact content
1155
+ * keeping the most severe duplicate, rank by severity, and cap at the
1156
+ * `CodeReview` concerns bound.
1157
+ */
1158
+ const rankAndDedupeConcerns = (concerns) => {
1159
+ const byContent = /* @__PURE__ */ new Map();
1160
+ for (const concern of concerns) {
1161
+ const key = `${concern.title}\u0000${concern.body}`;
1162
+ const previous = byContent.get(key);
1163
+ if (previous === void 0 || severityRank[concern.severity] < severityRank[previous.severity]) byContent.set(key, concern);
1164
+ }
1165
+ return [...byContent.values()].sort((left, right) => severityRank[left.severity] - severityRank[right.severity]).slice(0, 10);
1166
+ };
931
1167
  //#endregion
932
1168
  //#region src/internal/fan-out.ts
933
1169
  /** One discovery pass returns at most this many anchored candidates. */
@@ -936,8 +1172,14 @@ const MAX_CHILD_FINDINGS = 6;
936
1172
  const MAX_CHILD_CONCERNS = 3;
937
1173
  /** Every unit receives independent general and specialist discovery passes. */
938
1174
  const MAX_UNIT_CANDIDATES = 18;
939
- /** General + specialist discovery for every unit, then one verifier per unit. */
1175
+ /**
1176
+ * General + specialist discovery for every unit, then one verifier per unit.
1177
+ * The one-retry budget doubles the worst-case child Run count, but the
1178
+ * schedule itself never exceeds this bound.
1179
+ */
940
1180
  const MAX_REVIEW_CHILDREN = 24;
1181
+ /** Bounded structured concurrency across units; passes inside a unit are sequential. */
1182
+ const REVIEW_UNIT_CONCURRENCY = 4;
941
1183
  /** Structural minimum for a child that exposes no tools. */
942
1184
  const MAX_FILE_REVIEW_TOOL_CALLS = 1;
943
1185
  const ReviewWorkPhase = Schema.Literals(["discovery", "verification"]);
@@ -978,8 +1220,8 @@ var CandidateAssessment = class extends Schema.Class("@effect-agent/pr-review/Ca
978
1220
  }) {};
979
1221
  /**
980
1222
  * Exact suggestion settlement shape: a carried suggestion must be settled and
981
- * nothing else may be. Enforced identically by the live delegation projection
982
- * and the independent host coverage fold.
1223
+ * nothing else may be. A verification report that violates it is treated as a
1224
+ * misbehaving pass and retried within the pass budget.
983
1225
  */
984
1226
  const assessmentSettlesSuggestionExactly = (assessment, candidate) => candidate._tag === "FindingCandidate" && candidate.finding.suggestion !== void 0 ? assessment.suggestion !== void 0 : assessment.suggestion === void 0;
985
1227
  /**
@@ -1007,18 +1249,6 @@ const UnitPaths = Schema.Array(ChangedPath).check(Schema.isMinLength(1)).check(S
1007
1249
  const RiskCategories = Schema.Array(ReviewRiskCategory).check(Schema.isMaxLength(6));
1008
1250
  const Candidates = Schema.Array(ReviewCandidate).check(Schema.isMaxLength(18));
1009
1251
  const EvidenceShardIds = Schema.Array(ReviewEvidenceShardId).check(Schema.isMinLength(1)).check(Schema.isMaxLength(12));
1010
- /** Strict-object coordinator request for either discovery or verification. */
1011
- var FileReviewRequest = class extends Schema.Class("@effect-agent/pr-review/FileReviewRequest")({
1012
- phase: ReviewWorkPhase,
1013
- workId: ReviewPassId,
1014
- unitId: ReviewUnitId,
1015
- paths: UnitPaths,
1016
- evidenceShardIds: EvidenceShardIds,
1017
- perspective: ReviewWorkPerspective,
1018
- riskCategories: RiskCategories,
1019
- /** Empty for discovery; the exact discovered set for unit verification. */
1020
- candidates: Candidates
1021
- }) {};
1022
1252
  /** One complete host-selected evidence shard supplied to a review child. */
1023
1253
  var FileReviewEvidence = class extends Schema.Class("@effect-agent/pr-review/FileReviewEvidence")({
1024
1254
  shardId: ReviewEvidenceShardId,
@@ -1042,6 +1272,7 @@ var FileReviewBrief = class extends Schema.Class("@effect-agent/pr-review/FileRe
1042
1272
  evidenceShardIds: EvidenceShardIds,
1043
1273
  perspective: ReviewWorkPerspective,
1044
1274
  riskCategories: RiskCategories,
1275
+ /** Empty for discovery; the exact discovered set for unit verification. */
1045
1276
  candidates: Candidates,
1046
1277
  evidence: Schema.Array(FileReviewEvidence).check(Schema.isMinLength(1)).check(Schema.isMaxLength(12))
1047
1278
  }) {};
@@ -1055,24 +1286,16 @@ var FileReviewReport = class extends Schema.Class("@effect-agent/pr-review/FileR
1055
1286
  fileSummaries: Schema.Array(WalkthroughEntry).check(Schema.isMaxLength(12)),
1056
1287
  assessments: Schema.Array(CandidateAssessment).check(Schema.isMaxLength(18))
1057
1288
  }) {};
1058
- /** Bounded coordinator-visible result with host-assigned candidate IDs. */
1059
- var FileReviewUnitResult = class extends Schema.Class("@effect-agent/pr-review/FileReviewUnitResult")({
1060
- phase: ReviewWorkPhase,
1061
- workId: ReviewPassId,
1062
- unitId: ReviewUnitId,
1063
- candidates: Candidates,
1064
- fileSummaries: Schema.Array(WalkthroughEntry).check(Schema.isMaxLength(12)),
1065
- assessments: Schema.Array(CandidateAssessment).check(Schema.isMaxLength(18))
1066
- }) {};
1067
- var FileReviewUnitFailed = class extends Schema.TaggedError()("FileReviewUnitFailed", {
1068
- childErrorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256)),
1069
- message: Schema.String.check(Schema.isMaxLength(400))
1070
- }) {};
1071
- var FileReviewWorkRejected = class extends Schema.TaggedError()("FileReviewWorkRejected", {
1289
+ /**
1290
+ * A structurally valid child report that does not answer the scheduled pass:
1291
+ * wrong identity, phase-inapplicable fields, or an inexact assessment set.
1292
+ * Retried once like any other pass fault, because it is model misbehavior,
1293
+ * not evidence about the code under review.
1294
+ */
1295
+ var ReviewPassMisbehaved = class extends Schema.TaggedError()("ReviewPassMisbehaved", {
1072
1296
  workId: ReviewPassId,
1073
1297
  reason: Schema.NonEmptyString.check(Schema.isMaxLength(600))
1074
1298
  }) {};
1075
- const FileReviewFailure = Schema.Union([FileReviewUnitFailed, FileReviewWorkRejected]);
1076
1299
  const staticGuidanceLines = (guidance) => {
1077
1300
  if (guidance === void 0) return [];
1078
1301
  return (typeof guidance === "string" ? [guidance] : guidance).filter((line) => line.length > 0);
@@ -1110,8 +1333,6 @@ const makeFileReviewerInstructions = (options = {}) => (brief) => {
1110
1333
  };
1111
1334
  const fileReviewerInstructions = makeFileReviewerInstructions();
1112
1335
  const FileReviewToolkit = Toolkit.empty;
1113
- /** Compatibility export: the evidence-only child has no handler requirements. */
1114
- const FileReviewToolkitLayer = Layer.empty;
1115
1336
  const defaultFileReviewerPolicy = AgentPolicy.make({
1116
1337
  maxTurns: 6,
1117
1338
  maxToolCalls: 1,
@@ -1123,54 +1344,29 @@ const defaultFileReviewerPolicy = AgentPolicy.make({
1123
1344
  toolResultBounds: ToolResultBounds.make({ maxBytes: REVIEW_TOOL_RESULT_MAX_BYTES }),
1124
1345
  onExhaustion: "fail"
1125
1346
  });
1126
- const fileReviewPolicy = SubagentPolicy.make({
1127
- maxChildren: MAX_REVIEW_CHILDREN,
1128
- maxConcurrency: 4,
1129
- maxTurns: 6,
1130
- maxToolCalls: 1,
1131
- maxDuration: "6 minutes",
1132
- maxResultBytes: 256 * 1024
1133
- });
1134
- const mapFileReviewChildFailure = (failure) => FileReviewUnitFailed.make({
1135
- childErrorTag: failure._tag,
1136
- message: (failure.message ?? "").slice(0, 400)
1137
- });
1138
- const sameStrings = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
1139
- const rejectWork = (workId, reason) => FileReviewWorkRejected.make({
1140
- workId,
1141
- reason
1142
- });
1143
- /** Validate coordinator scheduling against the current deterministic plan. */
1144
- const prepareReviewBrief = (request) => Effect.gen(function* () {
1145
- const source = yield* PullRequestSource;
1146
- const mapSourceFailure = (failure) => rejectWork(request.workId, `pull-request source ${failure.operation} failed: ${failure.reason}`.slice(0, 600));
1147
- const files = yield* source.changedFiles.pipe(Effect.mapError(mapSourceFailure));
1148
- const plan = planReviewUnits(files, { totalChangedFiles: (yield* source.metadata.pipe(Effect.mapError(mapSourceFailure))).totalChangedFiles });
1149
- const unit = plan.units.find((candidate) => candidate.unitId === request.unitId);
1150
- if (unit === void 0 || !sameStrings(request.paths, unit.paths) || !sameStrings(request.evidenceShardIds, unit.evidenceShards.map((shard) => shard.shardId))) return yield* rejectWork(request.workId, "request does not match a host-planned unit");
1151
- if (request.phase === "discovery") {
1152
- const pass = plan.discoveryPasses.find((candidate) => candidate.passId === request.workId);
1153
- if (pass === void 0 || pass.unitId !== request.unitId || !sameStrings(pass.paths, request.paths) || !sameStrings(pass.evidenceShardIds, request.evidenceShardIds) || pass.perspective !== request.perspective || !sameStrings(pass.riskCategories, request.riskCategories) || request.candidates.length !== 0) return yield* rejectWork(request.workId, "discovery request does not match the host plan");
1154
- } else {
1155
- if (request.workId !== `${request.unitId}-verification` || request.perspective !== "candidate-verification" || !sameStrings(request.riskCategories, unit.riskCategories) || request.candidates.length === 0) return yield* rejectWork(request.workId, "verification request does not match the host-planned unit");
1156
- const candidateIds = /* @__PURE__ */ new Set();
1157
- const candidateSubjects = /* @__PURE__ */ new Set();
1158
- const allowed = new Set(unit.paths);
1159
- for (const candidate of request.candidates) {
1160
- const subjectKey = reviewCandidateSubjectKey(candidate);
1161
- if (candidateIds.has(candidate.candidateId) || candidateSubjects.has(subjectKey) || candidate.unitId !== unit.unitId || candidate.evidencePaths.some((path) => !allowed.has(path)) || candidate._tag === "FindingCandidate" && !allowed.has(candidate.finding.path)) return yield* rejectWork(request.workId, "verification candidates are duplicated or outside the planned unit");
1162
- candidateIds.add(candidate.candidateId);
1163
- candidateSubjects.add(subjectKey);
1164
- }
1347
+ const makeFileReviewerDefinition = (options = {}) => Agent.define("pr-review-worker", {
1348
+ input: FileReviewBrief,
1349
+ output: FileReviewReport,
1350
+ instructions: makeFileReviewerInstructions(options),
1351
+ toolkit: FileReviewToolkit,
1352
+ policy: defaultFileReviewerPolicy,
1353
+ description: "Perform one bounded discovery or independent candidate-verification pass over host-supplied pull-request evidence.",
1354
+ metadata: {
1355
+ deploymentClass: "E",
1356
+ surface: "read-only",
1357
+ stage: "discovery-verification"
1165
1358
  }
1359
+ });
1360
+ const FileReviewer = makeFileReviewerDefinition();
1361
+ const candidateOrdinal = (index) => String(index + 1).padStart(3, "0");
1362
+ /** Rebuild one unit's complete evidence from the same snapshot the plan used. */
1363
+ const unitEvidence = (unit, files) => Effect.gen(function* () {
1166
1364
  const byPath = new Map(files.map((file) => [file.path, file]));
1167
1365
  const evidence = [];
1168
1366
  for (const shard of unit.evidenceShards) {
1169
1367
  const file = byPath.get(shard.path);
1170
- if (file === void 0) return yield* rejectWork(request.workId, `planned evidence path is unavailable: ${shard.path}`);
1171
- const chunks = fileReviewEvidenceChunks(file);
1172
- const chunk = chunks[shard.ordinal - 1];
1173
- if (chunk === void 0 || chunks.length !== shard.total || chunk.annotatedPatch.length !== shard.evidenceChars) return yield* rejectWork(request.workId, `planned evidence shard no longer matches source: ${shard.shardId}`);
1368
+ const chunk = file === void 0 ? void 0 : fileReviewEvidenceChunks(file)[shard.ordinal - 1];
1369
+ if (file === void 0 || chunk === void 0) return yield* Effect.die(/* @__PURE__ */ new Error(`planned evidence shard has no source: ${shard.shardId} (${shard.path})`));
1174
1370
  evidence.push(FileReviewEvidence.make({
1175
1371
  shardId: shard.shardId,
1176
1372
  path: shard.path,
@@ -1181,171 +1377,274 @@ const prepareReviewBrief = (request) => Effect.gen(function* () {
1181
1377
  annotatedPatch: chunk.annotatedPatch
1182
1378
  }));
1183
1379
  }
1184
- return FileReviewBrief.make({
1185
- ...request,
1186
- evidence
1187
- });
1380
+ return evidence;
1188
1381
  });
1189
- const candidateOrdinal = (index) => String(index + 1).padStart(3, "0");
1190
- const projectReviewResult = (report, context, request) => {
1191
- if (context.budgetExhausted) return Effect.fail(rejectWork(report.workId, "review work exhausted its budget before exact settlement"));
1192
- if (report.phase !== request.phase || report.workId !== request.workId || report.unitId !== request.unitId) return Effect.fail(rejectWork(request.workId, "review output identity does not match the scheduled request"));
1193
- if (report.phase === "verification") {
1194
- if (report.findings.length > 0 || report.concerns.length > 0 || report.fileSummaries.length > 0) return Effect.fail(rejectWork(report.workId, "verification output contained discovery-only fields"));
1195
- const expectedById = new Map(request.candidates.map((candidate) => [candidate.candidateId, candidate]));
1196
- const assessedIds = /* @__PURE__ */ new Set();
1197
- for (const assessment of report.assessments) {
1198
- const candidate = expectedById.get(assessment.candidateId);
1199
- if (candidate === void 0 || assessedIds.has(assessment.candidateId)) return Effect.fail(rejectWork(report.workId, "verification output did not assess the exact candidate set"));
1200
- if (!assessmentSettlesSuggestionExactly(assessment, candidate)) return Effect.fail(rejectWork(report.workId, "verification output did not settle suggestion publication exactly"));
1201
- assessedIds.add(assessment.candidateId);
1382
+ const misbehaved = (workId, reason) => ReviewPassMisbehaved.make({
1383
+ workId,
1384
+ reason: reason.slice(0, 600)
1385
+ });
1386
+ /** Validate that a verification report assesses exactly the scheduled candidates. */
1387
+ const validateVerificationReport = (brief, report) => {
1388
+ if (report.findings.length > 0 || report.concerns.length > 0 || report.fileSummaries.length > 0) return misbehaved(brief.workId, "verification output contained discovery-only fields");
1389
+ const expectedById = new Map(brief.candidates.map((candidate) => [candidate.candidateId, candidate]));
1390
+ const assessedIds = /* @__PURE__ */ new Set();
1391
+ for (const assessment of report.assessments) {
1392
+ const candidate = expectedById.get(assessment.candidateId);
1393
+ if (candidate === void 0 || assessedIds.has(assessment.candidateId)) return misbehaved(brief.workId, "verification output did not assess the exact candidate set");
1394
+ if (!assessmentSettlesSuggestionExactly(assessment, candidate)) return misbehaved(brief.workId, "verification output did not settle suggestion publication exactly");
1395
+ assessedIds.add(assessment.candidateId);
1396
+ }
1397
+ if (assessedIds.size !== expectedById.size) return misbehaved(brief.workId, "verification output did not assess the exact candidate set");
1398
+ };
1399
+ /**
1400
+ * Run one scheduled pass: execute the child, decode its report, and enforce
1401
+ * the pass contract. Any typed fault — child failure, malformed or misdirected
1402
+ * output — is retried once; budget exhaustion is terminal because a retry
1403
+ * would fail the same way. The settled outcome is a value either way, so one
1404
+ * flaky pass can never fail the whole pipeline.
1405
+ */
1406
+ const runReviewPass = (binding, brief, budget) => Effect.gen(function* () {
1407
+ const result = yield* AgentRuntime.run(binding, brief, {
1408
+ ...budget === void 0 ? {} : { budget },
1409
+ estimateCostMicrousd: () => Effect.succeed(500)
1410
+ });
1411
+ const report = yield* Schema.decodeUnknownEffect(FileReviewReport)(result.output).pipe(Effect.mapError((error) => misbehaved(brief.workId, `child report failed to decode: ${error.message}`)));
1412
+ if (report.phase !== brief.phase || report.workId !== brief.workId || report.unitId !== brief.unitId) return yield* misbehaved(brief.workId, "child report identity does not match the scheduled pass");
1413
+ if (brief.phase === "verification") {
1414
+ const violation = validateVerificationReport(brief, report);
1415
+ if (violation !== void 0) return yield* violation;
1416
+ } else if (report.assessments.length > 0) return yield* misbehaved(brief.workId, "discovery output contained verification-only assessments");
1417
+ return {
1418
+ report,
1419
+ turns: result.turns
1420
+ };
1421
+ }).pipe(Effect.scoped, Effect.retry({
1422
+ times: 1,
1423
+ while: (error) => error._tag !== "BudgetExceeded"
1424
+ }), Effect.map((settled) => ({
1425
+ _tag: "settled",
1426
+ ...settled
1427
+ })), Effect.catch((error) => Effect.succeed({
1428
+ _tag: "failed",
1429
+ errorTag: String(error._tag).slice(0, 256)
1430
+ })));
1431
+ /**
1432
+ * Keep only findings anchored inside the pass's exact assigned evidence and
1433
+ * concerns bound to unit paths. Everything else is discarded and counted —
1434
+ * an invalid anchor invalidates one claim, never the pass that produced it.
1435
+ */
1436
+ const harvestDiscovery = (pass, unit, files, anchorFiles, report) => {
1437
+ const allowed = new Set(pass.paths);
1438
+ let discarded = 0;
1439
+ const keptFindings = [];
1440
+ for (const finding of report.findings) {
1441
+ if (!allowed.has(finding.path) || anchorViolation(finding, anchorFiles) !== void 0 || !findingAnchorInUnitEvidence(finding, unit, files)) {
1442
+ discarded += 1;
1443
+ continue;
1202
1444
  }
1203
- if (assessedIds.size !== expectedById.size) return Effect.fail(rejectWork(report.workId, "verification output did not assess the exact candidate set"));
1204
- return Effect.succeed(FileReviewUnitResult.make({
1205
- phase: report.phase,
1206
- workId: report.workId,
1207
- unitId: report.unitId,
1208
- candidates: [],
1209
- fileSummaries: [],
1210
- assessments: report.assessments
1211
- }));
1445
+ keptFindings.push(finding);
1212
1446
  }
1213
- if (report.assessments.length > 0) return Effect.fail(rejectWork(report.workId, "discovery output contained verification-only assessments"));
1214
- const allowed = new Set(request.paths);
1215
- if (report.findings.some((finding) => !allowed.has(finding.path)) || report.concerns.some((candidate) => candidate.evidencePaths.some((path) => !allowed.has(path))) || report.fileSummaries.some((entry) => !allowed.has(entry.path))) return Effect.fail(rejectWork(report.workId, "discovery output referenced evidence outside the scheduled unit"));
1216
- return Effect.gen(function* () {
1217
- const source = yield* PullRequestSource;
1218
- const mapSourceFailure = (failure) => rejectWork(request.workId, `pull-request source ${failure.operation} failed: ${failure.reason}`.slice(0, 600));
1219
- const files = yield* source.changedFiles.pipe(Effect.mapError(mapSourceFailure));
1220
- const metadata = yield* source.metadata.pipe(Effect.mapError(mapSourceFailure));
1221
- const anchorFiles = yield* source.anchorFiles.pipe(Effect.mapError(mapSourceFailure));
1222
- const unit = planReviewUnits(files, { totalChangedFiles: metadata.totalChangedFiles }).units.find((candidate) => candidate.unitId === request.unitId);
1223
- if (unit === void 0) return yield* rejectWork(request.workId, "scheduled review unit is no longer available");
1224
- for (const finding of report.findings) {
1225
- const violation = anchorViolation(finding, anchorFiles);
1226
- if (violation !== void 0 || !findingAnchorInUnitEvidence(finding, unit, files)) return yield* rejectWork(request.workId, `discovery finding has no valid anchor in its assigned evidence: ${violation ?? finding.path}`);
1447
+ const keptConcerns = [];
1448
+ for (const candidate of report.concerns) {
1449
+ if (candidate.evidencePaths.some((path) => !allowed.has(path))) {
1450
+ discarded += 1;
1451
+ continue;
1227
1452
  }
1228
- const findingCandidates = report.findings.map((finding, index) => FindingCandidate.make({
1229
- candidateId: `${request.workId}:finding:${candidateOrdinal(index)}`,
1230
- workId: request.workId,
1231
- unitId: request.unitId,
1453
+ keptConcerns.push(candidate);
1454
+ }
1455
+ return {
1456
+ candidates: [...keptFindings.map((finding, index) => FindingCandidate.make({
1457
+ candidateId: `${pass.passId}:finding:${candidateOrdinal(index)}`,
1458
+ workId: pass.passId,
1459
+ unitId: pass.unitId,
1232
1460
  finding,
1233
1461
  evidencePaths: [finding.path]
1234
- }));
1235
- const concernCandidates = report.concerns.map((candidate, index) => ConcernCandidate.make({
1236
- candidateId: `${request.workId}:concern:${candidateOrdinal(index)}`,
1237
- workId: request.workId,
1238
- unitId: request.unitId,
1462
+ })), ...keptConcerns.map((candidate, index) => ConcernCandidate.make({
1463
+ candidateId: `${pass.passId}:concern:${candidateOrdinal(index)}`,
1464
+ workId: pass.passId,
1465
+ unitId: pass.unitId,
1239
1466
  concern: candidate.concern,
1240
1467
  evidencePaths: candidate.evidencePaths
1241
- }));
1242
- return FileReviewUnitResult.make({
1243
- phase: report.phase,
1244
- workId: report.workId,
1245
- unitId: report.unitId,
1246
- candidates: [...findingCandidates, ...concernCandidates],
1247
- fileSummaries: report.fileSummaries,
1248
- assessments: []
1249
- });
1250
- });
1251
- };
1252
- const delegationDescription = "Run exactly one host-planned discovery or candidate-verification child. Copy every plan field and candidate verbatim; never retry failed work.";
1253
- const makeFileReviewDelegation = (child) => Subagent.define("delegate_file_review", {
1254
- description: delegationDescription,
1255
- target: child,
1256
- parameters: FileReviewRequest,
1257
- success: FileReviewUnitResult,
1258
- failure: FileReviewFailure,
1259
- failureMode: "return",
1260
- prepareInput: prepareReviewBrief,
1261
- projectResult: projectReviewResult,
1262
- policy: fileReviewPolicy
1263
- });
1264
- var ListReviewUnitsQuery = class extends Schema.Class("@effect-agent/pr-review/ListReviewUnitsQuery")({ scope: Schema.Literal("all") }) {};
1265
- const ListReviewUnits = Tool.make("list_review_units", {
1266
- description: "List deterministic bounded review units, explicit risk categories, every required discovery pass, and paths the pipeline cannot cover.",
1267
- parameters: ListReviewUnitsQuery,
1268
- success: ReviewUnitPlan,
1269
- failure: PullRequestSourceFailure,
1270
- failureMode: "error",
1271
- dependencies: [PullRequestSource]
1272
- }).annotate(ToolExecutionClass, "readonly");
1273
- const FanOutCoordinatorToolkit = Toolkit.make(ListReviewUnits);
1274
- const FanOutCoordinatorToolkitLayer = FanOutCoordinatorToolkit.toLayer({ list_review_units: () => Effect.gen(function* () {
1275
- const source = yield* PullRequestSource;
1276
- return planReviewUnits(yield* source.changedFiles, { totalChangedFiles: (yield* source.metadata).totalChangedFiles });
1277
- }) });
1278
- const makeFanOutReviewInstructions = (options = {}) => (mission) => {
1279
- const maxFindings = clampMaxFindings(options.maxFindings);
1280
- return [
1281
- `You coordinate the bounded multi-pass review of pull request #${mission.number} ("${mission.title}") in ${mission.repository}.`,
1282
- mission.body.length > 0 ? `Author description:\n${mission.body}` : "No author description.",
1283
- ...staticGuidanceLines(options.guidance),
1284
- "1. Call list_review_units exactly once.",
1285
- "2. For EVERY discoveryPass, call delegate_file_review exactly once with phase \"discovery\", workId=passId, and the pass unitId/paths/evidenceShardIds/perspective/riskCategories verbatim; candidates must be []. Prefer one bounded parallel batch. Never retry.",
1286
- "3. Group candidates returned by all successful discovery passes by unit. Deterministically deduplicate byte-identical finding or concern payloads, retaining the first candidate in discoveryPass plan order. For every unit with at least one retained candidate, call delegate_file_review exactly once with phase \"verification\", workId \"<unitId>-verification\", perspective \"candidate-verification\", the unit paths/evidenceShardIds/riskCategories, and EVERY retained candidate copied byte-for-byte. Prefer one bounded parallel batch. Never retry.",
1287
- "4. Verification is authoritative: rejected candidates must not be reported. The host independently reconstructs publishable findings from exact confirmed assessments, so do not select, rewrite, downgrade, or invent findings.",
1288
- `5. Return ONLY CodeReview JSON. Write a concise summary of completed and failed stages. Set findings=[] and concerns=[]; the host injects exact confirmed candidates. Copy factual fileSummaries into walkthrough without invention. The host publication cap is ${maxFindings}.`,
1289
- "No configured pipeline can prove absence of defects. Describe settled work, never an exhaustive or defect-free review."
1290
- ].join("\n");
1468
+ }))],
1469
+ fileSummaries: report.fileSummaries.filter((entry) => allowed.has(entry.path)),
1470
+ discarded
1471
+ };
1291
1472
  };
1292
- const fanOutReviewInstructions = makeFanOutReviewInstructions();
1293
- const defaultFanOutPolicy = AgentPolicy.make({
1294
- maxTurns: 7,
1295
- maxToolCalls: 25,
1296
- maxDuration: "20 minutes",
1297
- toolConcurrency: 4,
1298
- repeatedFailureLimit: 3,
1299
- tokenBudget: 4e5,
1300
- contextTokenLimit: 15e4,
1301
- onExhaustion: "final-answer"
1302
- });
1303
- const makeFileReviewerDefinition = (options = {}) => Agent.define("pr-review-worker", {
1304
- input: FileReviewBrief,
1305
- output: FileReviewReport,
1306
- instructions: makeFileReviewerInstructions(options),
1307
- toolkit: FileReviewToolkit,
1308
- policy: defaultFileReviewerPolicy,
1309
- description: "Perform one bounded discovery or independent candidate-verification pass over host-supplied pull-request evidence.",
1310
- metadata: {
1311
- deploymentClass: "E",
1312
- surface: "read-only",
1313
- stage: "discovery-verification"
1473
+ const reviewUnit = (binding, unit, passes, input) => Effect.gen(function* () {
1474
+ const evidence = yield* unitEvidence(unit, input.files);
1475
+ const failedPasses = [];
1476
+ const candidates = [];
1477
+ const subjects = /* @__PURE__ */ new Set();
1478
+ const walkthrough = [];
1479
+ let discardedFindings = 0;
1480
+ let turns = 0;
1481
+ let completedGeneralPasses = 0;
1482
+ let completedSpecialistPasses = 0;
1483
+ for (const pass of passes) {
1484
+ const stage = pass.perspective === "risk-specialist" ? "specialist" : "discovery";
1485
+ const brief = FileReviewBrief.make({
1486
+ phase: "discovery",
1487
+ workId: pass.passId,
1488
+ unitId: pass.unitId,
1489
+ paths: pass.paths,
1490
+ evidenceShardIds: pass.evidenceShardIds,
1491
+ perspective: pass.perspective,
1492
+ riskCategories: pass.riskCategories,
1493
+ candidates: [],
1494
+ evidence
1495
+ });
1496
+ const outcome = yield* runReviewPass(binding, brief, input.budget);
1497
+ if (outcome._tag === "failed") {
1498
+ failedPasses.push(FailedReviewPass.make({
1499
+ workId: pass.passId,
1500
+ stage,
1501
+ errorTag: outcome.errorTag
1502
+ }));
1503
+ continue;
1504
+ }
1505
+ turns += outcome.turns;
1506
+ if (stage === "specialist") completedSpecialistPasses += 1;
1507
+ else completedGeneralPasses += 1;
1508
+ const harvest = harvestDiscovery(pass, unit, input.files, input.anchorFiles, outcome.report);
1509
+ discardedFindings += harvest.discarded;
1510
+ if (pass.perspective === "general") walkthrough.push(...harvest.fileSummaries);
1511
+ for (const candidate of harvest.candidates) {
1512
+ const subject = reviewCandidateSubjectKey(candidate);
1513
+ if (subjects.has(subject)) continue;
1514
+ subjects.add(subject);
1515
+ candidates.push(candidate);
1516
+ }
1314
1517
  }
1315
- });
1316
- const delegationToolFor = (delegation) => delegation.tool.annotate(ToolExecutionClass, "readonly");
1317
- const makeFanOutReviewerDefinition = (options, delegation) => Agent.define("pr-fanout-reviewer", {
1318
- input: ReviewMission,
1319
- output: CodeReview,
1320
- instructions: makeFanOutReviewInstructions(options),
1321
- toolkit: Toolkit.make(ListReviewUnits, delegationToolFor(delegation)),
1322
- policy: defaultFanOutPolicy,
1323
- description: "Coordinate deterministic general/specialist discovery and independent candidate verification over bounded review units.",
1324
- metadata: {
1325
- deploymentClass: "E",
1326
- surface: "read-only",
1327
- delegation: "S1-attached",
1328
- assurance: "multi-pass"
1518
+ const confirmed = [];
1519
+ let rejectedCandidates = 0;
1520
+ let unsettledCandidates = 0;
1521
+ let completedVerificationPasses = 0;
1522
+ const requiredVerificationPasses = candidates.length > 0 ? 1 : 0;
1523
+ if (candidates.length > 0) {
1524
+ const workId = `${unit.unitId}-verification`;
1525
+ const brief = FileReviewBrief.make({
1526
+ phase: "verification",
1527
+ workId,
1528
+ unitId: unit.unitId,
1529
+ paths: unit.paths,
1530
+ evidenceShardIds: unit.evidenceShards.map((shard) => shard.shardId),
1531
+ perspective: "candidate-verification",
1532
+ riskCategories: unit.riskCategories,
1533
+ candidates,
1534
+ evidence
1535
+ });
1536
+ const outcome = yield* runReviewPass(binding, brief, input.budget);
1537
+ if (outcome._tag === "failed") {
1538
+ unsettledCandidates = candidates.length;
1539
+ failedPasses.push(FailedReviewPass.make({
1540
+ workId,
1541
+ stage: "verification",
1542
+ errorTag: outcome.errorTag
1543
+ }));
1544
+ } else {
1545
+ turns += outcome.turns;
1546
+ completedVerificationPasses = 1;
1547
+ const byId = new Map(candidates.map((candidate) => [candidate.candidateId, candidate]));
1548
+ for (const assessment of outcome.report.assessments) {
1549
+ const candidate = byId.get(assessment.candidateId);
1550
+ if (candidate === void 0) continue;
1551
+ if (assessment.disposition === "confirmed") confirmed.push({
1552
+ assessment,
1553
+ candidate
1554
+ });
1555
+ else rejectedCandidates += 1;
1556
+ }
1557
+ }
1329
1558
  }
1330
- });
1331
- const makeFanOutReviewSuite = (options = {}) => {
1332
- const child = makeFileReviewerDefinition({ guidance: options.guidance });
1333
- const delegation = makeFileReviewDelegation(child);
1334
1559
  return {
1335
- child,
1336
- parent: makeFanOutReviewerDefinition(options, delegation),
1337
- delegation
1560
+ failedPasses,
1561
+ discoveredCandidates: candidates.length,
1562
+ confirmed,
1563
+ rejectedCandidates,
1564
+ unsettledCandidates,
1565
+ discardedFindings,
1566
+ walkthrough,
1567
+ turns,
1568
+ completedGeneralPasses,
1569
+ completedSpecialistPasses,
1570
+ requiredVerificationPasses,
1571
+ completedVerificationPasses,
1572
+ unreviewedPaths: failedPasses.length > 0 ? unit.paths : []
1338
1573
  };
1574
+ });
1575
+ const countNoun = (count, noun) => `${count} ${noun}${count === 1 ? "" : "s"}`;
1576
+ const composeSummary = (plan, assurance) => {
1577
+ const requiredDiscovery = assurance.requiredGeneralDiscoveryPasses + assurance.requiredSpecialistPasses;
1578
+ const completedDiscovery = assurance.completedGeneralDiscoveryPasses + assurance.completedSpecialistPasses;
1579
+ const parts = [`Reviewed ${countNoun(plan.totalFiles, "changed file")} across ${countNoun(plan.units.length, "bounded unit")}: ${completedDiscovery}/${requiredDiscovery} discovery and ${assurance.completedVerificationPasses}/${assurance.requiredVerificationPasses} verification pass(es) settled; ${assurance.confirmedCandidates} of ${countNoun(assurance.discoveredCandidates, "discovered candidate")} confirmed by independent verification.`];
1580
+ if (assurance.failedPasses.length > 0) parts.push(`${countNoun(assurance.failedPasses.length, "pass")} did not settle; the affected paths are carried forward and retried on the next run. This is a reviewer-side gap, not a code defect.`);
1581
+ if (assurance.discardedInvalidFindings > 0) parts.push(`${countNoun(assurance.discardedInvalidFindings, "candidate")} discarded for anchors or paths outside the assigned evidence.`);
1582
+ if (plan.undiffablePaths.length > 0) parts.push(`${countNoun(plan.undiffablePaths.length, "path")} had no reviewable textual evidence and keep input coverage incomplete; exclude such paths with ignore globs when that is intended.`);
1583
+ if (plan.unassignedPaths.length > 0 || plan.unassignedEvidenceShardCount > 0) parts.push("The changeset exceeded the bounded fan-out capacity; unassigned scope is reported under input coverage.");
1584
+ parts.push("No configured pipeline can prove absence of defects; this describes settled work only.");
1585
+ return parts.join(" ").slice(0, 4e3);
1339
1586
  };
1340
- const defaultSuite = makeFanOutReviewSuite();
1341
- const FileReviewer = defaultSuite.child;
1342
- const FanOutReviewer = defaultSuite.parent;
1343
- const fileReviewDelegation = defaultSuite.delegation;
1344
- const DelegateFileReview = delegationToolFor(fileReviewDelegation);
1345
- const FanOutReviewToolkit = FanOutReviewer.toolkit;
1346
- const FileReviewDelegationFailure = fileReviewDelegation.containedFailure;
1347
- const fanOutHandlersLayerFor = (delegation) => (childBinding) => SubagentRuntime.layer(delegation, childBinding, { mapChildFailure: mapFileReviewChildFailure });
1348
- const fanOutHandlersLayer = fanOutHandlersLayerFor(fileReviewDelegation);
1587
+ /**
1588
+ * Run the complete host-scheduled fan-out pipeline over one selected
1589
+ * changeset snapshot: plan, independent discovery, exact verification, and a
1590
+ * deterministic host-composed CodeReview from verifier-confirmed candidates
1591
+ * only. The verdict is derived from confirmed severities, never model prose.
1592
+ */
1593
+ const runFanOutReview = (binding, input) => Effect.gen(function* () {
1594
+ const plan = planReviewUnits(input.files, { totalChangedFiles: input.totalChangedFiles });
1595
+ const passesByUnit = /* @__PURE__ */ new Map();
1596
+ for (const pass of plan.discoveryPasses) {
1597
+ const passes = passesByUnit.get(pass.unitId) ?? [];
1598
+ passes.push(pass);
1599
+ passesByUnit.set(pass.unitId, passes);
1600
+ }
1601
+ const outcomes = yield* Effect.forEach(plan.units, (unit) => reviewUnit(binding, unit, passesByUnit.get(unit.unitId) ?? [], input), { concurrency: 4 });
1602
+ const failedPasses = outcomes.flatMap((outcome) => outcome.failedPasses);
1603
+ const unsettledCandidates = outcomes.reduce((total, outcome) => total + outcome.unsettledCandidates, 0);
1604
+ const reasons = [];
1605
+ if (failedPasses.length > 0) reasons.push(boundedListReason("configured review passes did not settle", failedPasses.map((pass) => `${pass.workId} (${pass.errorTag})`)));
1606
+ if (unsettledCandidates > 0) reasons.push(`${unsettledCandidates} discovered candidate(s) did not receive exact verification`);
1607
+ const requiredSpecialistPasses = plan.discoveryPasses.filter((pass) => pass.perspective === "risk-specialist").length;
1608
+ const confirmed = outcomes.flatMap((outcome) => outcome.confirmed);
1609
+ const assurance = ReviewAssurance.make({
1610
+ status: reasons.length === 0 ? "settled" : "incomplete",
1611
+ requiredGeneralDiscoveryPasses: plan.discoveryPasses.length - requiredSpecialistPasses,
1612
+ completedGeneralDiscoveryPasses: outcomes.reduce((total, outcome) => total + outcome.completedGeneralPasses, 0),
1613
+ requiredSpecialistPasses,
1614
+ completedSpecialistPasses: outcomes.reduce((total, outcome) => total + outcome.completedSpecialistPasses, 0),
1615
+ requiredVerificationPasses: outcomes.reduce((total, outcome) => total + outcome.requiredVerificationPasses, 0),
1616
+ completedVerificationPasses: outcomes.reduce((total, outcome) => total + outcome.completedVerificationPasses, 0),
1617
+ discoveredCandidates: outcomes.reduce((total, outcome) => total + outcome.discoveredCandidates, 0),
1618
+ confirmedCandidates: confirmed.length,
1619
+ rejectedCandidates: outcomes.reduce((total, outcome) => total + outcome.rejectedCandidates, 0),
1620
+ unsettledCandidates,
1621
+ discardedInvalidFindings: outcomes.reduce((total, outcome) => total + outcome.discardedFindings, 0),
1622
+ failedPasses,
1623
+ reasons
1624
+ });
1625
+ const findings = rankAndDedupeFindings(confirmed.flatMap(({ assessment, candidate }) => candidate._tag === "FindingCandidate" ? [confirmedFindingForPublication(assessment, candidate)] : []));
1626
+ const concerns = rankAndDedupeConcerns(confirmed.flatMap(({ candidate }) => candidate._tag === "ConcernCandidate" ? [candidate.concern] : []));
1627
+ const walkthrough = outcomes.flatMap((outcome) => outcome.walkthrough);
1628
+ const blocking = findings.some((finding) => finding.severity === "blocking") || concerns.some((concern) => concern.severity === "blocking");
1629
+ return {
1630
+ review: CodeReview.make({
1631
+ summary: composeSummary(plan, assurance),
1632
+ verdict: blocking ? "request-changes" : findings.length > 0 || concerns.length > 0 ? "comment" : "approve",
1633
+ findings,
1634
+ ...concerns.length === 0 ? {} : { concerns },
1635
+ ...walkthrough.length === 0 ? {} : { walkthrough }
1636
+ }),
1637
+ assurance,
1638
+ plan,
1639
+ unreviewedPaths: [.../* @__PURE__ */ new Set([
1640
+ ...outcomes.flatMap((outcome) => outcome.unreviewedPaths),
1641
+ ...plan.unassignedPaths,
1642
+ ...plan.partialEvidencePaths,
1643
+ ...plan.undiffablePaths
1644
+ ])].sort(),
1645
+ turns: outcomes.reduce((total, outcome) => total + outcome.turns, 0)
1646
+ };
1647
+ });
1349
1648
  //#endregion
1350
1649
  //#region src/internal/fingerprint.ts
1351
1650
  const MARKER_PREFIX = "<!-- effect-agent-pr-review fingerprint=sha256:";
@@ -1410,16 +1709,21 @@ var StoredReviewConcern = class extends Schema.Class("@effect-agent/pr-review/St
1410
1709
  title: Schema.NonEmptyString.check(Schema.isMaxLength(120)),
1411
1710
  body: StoredText
1412
1711
  }) {};
1712
+ /** The carried-scope bound; a run that cannot fit its leftovers publishes no state. */
1713
+ const MAX_STORED_UNREVIEWED_PATHS = 100;
1413
1714
  /**
1414
- * Versioned state embedded only after complete input assignment and settled
1415
- * configured review assurance. The head plus full-scope fingerprint forms an
1416
- * incremental baseline; an absent unresolved item never means the path is
1417
- * defect-free. The `acceptedScopeFingerprint` name is retained for wire
1418
- * compatibility. Storing hundreds of path strings separately would not fit
1419
- * GitHub's bounded review body in the worst case.
1715
+ * Versioned state embedded after EVERY completed run that can be signed. The
1716
+ * head plus full-scope fingerprint forms an incremental baseline; an absent
1717
+ * unresolved item never means the path is defect-free. `unreviewedPaths`
1718
+ * carries retryable review gaps (failed passes) forward so the next
1719
+ * incremental run re-reviews exactly them plus the new delta — the baseline
1720
+ * advances monotonically instead of freezing on one flaky pass and reopening
1721
+ * the whole post-baseline scope. The `acceptedScopeFingerprint` name is
1722
+ * retained for wire compatibility. Storing hundreds of path strings
1723
+ * separately would not fit GitHub's bounded review body in the worst case.
1420
1724
  */
1421
1725
  var ReviewState = class extends Schema.Class("@effect-agent/pr-review/ReviewState")({
1422
- version: Schema.Literal(1),
1726
+ version: Schema.Literal(2),
1423
1727
  repository: Schema.NonEmptyString.check(Schema.isMaxLength(200)),
1424
1728
  pullRequestNumber: Schema.Int.check(Schema.isGreaterThan(0)),
1425
1729
  baseRef: Schema.NonEmptyString.check(Schema.isMaxLength(300)),
@@ -1434,6 +1738,14 @@ var ReviewState = class extends Schema.Class("@effect-agent/pr-review/ReviewStat
1434
1738
  })),
1435
1739
  unresolvedFindings: Schema.Array(StoredReviewFinding).check(Schema.isMaxLength(20)),
1436
1740
  unresolvedConcerns: Schema.Array(StoredReviewConcern).check(Schema.isMaxLength(10)),
1741
+ /** Retryable review gaps carried into the next incremental run's scope. */
1742
+ unreviewedPaths: Schema.Array(ChangedPath).check(Schema.isMaxLength(100)),
1743
+ /**
1744
+ * True only when the producing run had complete input coverage, no
1745
+ * unsettled pass, and nothing carried. Skip-unchanged authority: an
1746
+ * unchanged patch may skip re-review only over a settled state.
1747
+ */
1748
+ settled: Schema.Boolean,
1437
1749
  lastReviewMode: ReviewScopeMode
1438
1750
  }) {};
1439
1751
  const toStoredFinding = (finding) => StoredReviewFinding.make({
@@ -1462,12 +1774,12 @@ const fromStoredConcern = (concern) => ReviewConcern.make({
1462
1774
  title: concern.title,
1463
1775
  body: concern.body
1464
1776
  });
1465
- const STATE_MARKER_PREFIX = "<!-- effect-agent-pr-review state-v1:";
1777
+ const STATE_MARKER_PREFIX = "<!-- effect-agent-pr-review state-v2:";
1466
1778
  const STATE_MARKER_SUFFIX = " -->";
1467
- const STATE_MARKER_PATTERN = /(?:^|\n)<!-- effect-agent-pr-review state-v1:([A-Za-z0-9+/]+={0,2})\.([0-9a-f]{64}) -->$/;
1468
- const STATE_SIGNATURE_DOMAIN = "effect-agent-pr-review/state-v1\0";
1779
+ const STATE_MARKER_PATTERN = /(?:^|\n)<!-- effect-agent-pr-review state-v2:([A-Za-z0-9+/]+={0,2})\.([0-9a-f]{64}) -->$/;
1780
+ const STATE_SIGNATURE_DOMAIN = "effect-agent-pr-review/state-v2\0";
1469
1781
  const MAX_REVIEW_STATE_MARKER_CHARS = 24e3;
1470
- const ReviewStateMarker = Schema.NonEmptyString.check(Schema.isMaxLength(MAX_REVIEW_STATE_MARKER_CHARS), Schema.isPattern(/^<!-- effect-agent-pr-review state-v1:[A-Za-z0-9+/]+={0,2}\.[0-9a-f]{64} -->$/)).pipe(Schema.brand("@effect-agent/pr-review/ReviewStateMarker"));
1782
+ const ReviewStateMarker = Schema.NonEmptyString.check(Schema.isMaxLength(MAX_REVIEW_STATE_MARKER_CHARS), Schema.isPattern(/^<!-- effect-agent-pr-review state-v2:[A-Za-z0-9+/]+={0,2}\.[0-9a-f]{64} -->$/)).pipe(Schema.brand("@effect-agent/pr-review/ReviewStateMarker"));
1471
1783
  var ReviewStateAuthenticationFailure = class extends Schema.TaggedError()("ReviewStateAuthenticationFailure", {
1472
1784
  operation: Schema.Literals(["sign", "verify"]),
1473
1785
  reason: Schema.NonEmptyString.check(Schema.isMaxLength(2048))
@@ -1620,13 +1932,17 @@ const selectReviewRange = (input) => {
1620
1932
  const currentPaths = new Set(input.fullFiles.flatMap((file) => file.previousPath === void 0 ? [file.path] : [file.path, file.previousPath]));
1621
1933
  const selectedByPath = /* @__PURE__ */ new Map();
1622
1934
  for (const file of comparison.files) if (currentPaths.has(file.path) || file.previousPath !== void 0 && currentPaths.has(file.previousPath)) selectedByPath.set(file.path, file);
1623
- if (input.priorState.baseSha !== input.current.baseSha) {
1624
- for (const file of input.fullFiles) if (affectedPaths.has(file.path) || file.previousPath !== void 0 && affectedPaths.has(file.previousPath)) selectedByPath.set(file.path, file);
1935
+ const carriedPaths = new Set(input.priorState.unreviewedPaths.filter((path) => currentPaths.has(path)));
1936
+ for (const path of carriedPaths) affectedPaths.add(path);
1937
+ const rescuePaths = input.priorState.baseSha !== input.current.baseSha;
1938
+ if (rescuePaths || carriedPaths.size > 0) {
1939
+ for (const file of input.fullFiles) if (rescuePaths && (affectedPaths.has(file.path) || file.previousPath !== void 0 && affectedPaths.has(file.previousPath)) || carriedPaths.has(file.path) || file.previousPath !== void 0 && carriedPaths.has(file.previousPath)) selectedByPath.set(file.path, file);
1625
1940
  }
1626
1941
  const selectedFiles = [...selectedByPath.values()].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
1942
+ const carriedReason = carriedPaths.size === 0 ? "" : `; retrying ${carriedPaths.size} carried unreviewed path(s)`;
1627
1943
  return {
1628
1944
  mode: "incremental",
1629
- reason: `changes since settled review head ${input.priorState.reviewedHeadSha.slice(0, 7)}${baseReason}`,
1945
+ reason: `changes since reviewed head ${input.priorState.reviewedHeadSha.slice(0, 7)}${baseReason}${carriedReason}`,
1630
1946
  files: selectedFiles,
1631
1947
  affectedPaths: [...affectedPaths].sort(),
1632
1948
  totalFiles: selectedFiles.length,
@@ -1725,7 +2041,7 @@ var ReviewRetirementReport = class extends Schema.Class("@effect-agent/pr-review
1725
2041
  const findingIdentity = (finding) => `${finding.path}\u0000${finding.startLine}\u0000${finding.endLine}\u0000${finding.title}`;
1726
2042
  const REVIEW_METADATA_PATTERN = /<!-- effect-agent-pr-review metadata\n[\s\S]*?\n-->/g;
1727
2043
  const FINGERPRINT_PATTERN = /<!-- effect-agent-pr-review fingerprint=sha256:[0-9a-f]{64} -->/g;
1728
- const STATE_PATTERN = /<!-- effect-agent-pr-review state-v1:[A-Za-z0-9+/]+={0,2}\.[0-9a-f]{64} -->/g;
2044
+ const STATE_PATTERN = /<!-- effect-agent-pr-review state-v\d+:[A-Za-z0-9+/]+={0,2}\.[0-9a-f]{64} -->/g;
1729
2045
  const RETIRED_ORIGINAL_PATTERN = /<!-- effect-agent-pr-review retired-original:start -->\n([\s\S]*?)\n<!-- effect-agent-pr-review retired-original:end -->/;
1730
2046
  const MACHINE_COMMENT_PATTERN = new RegExp(`${REVIEW_METADATA_PATTERN.source}|${FINGERPRINT_PATTERN.source}|${STATE_PATTERN.source}`, "g");
1731
2047
  const VERDICT_CALLOUT_PATTERN = /^(?:> \[!(?:CAUTION|IMPORTANT)\]\n> [^\n]*(?:\n> [^\n]*)*|> (?:ℹ️|✅)[^\n]*)\n*/;
@@ -2298,6 +2614,6 @@ const fingerprintUnchanged = (current) => Effect.gen(function* () {
2298
2614
  return Option.isSome(latest) && latest.value === current;
2299
2615
  });
2300
2616
  //#endregion
2301
- export { DiscoveredConcern as $, normalizeRepoRelativePath as $n, ReviewPassId as $t, ReviewStateAuthenticator as A, ReviewConcern as An, defaultFanOutPolicy as At, selectedPullRequestSourceLayer as B, fileReviewEvidenceChunks as Bn, makeFileReviewerInstructions as Bt, MAX_REVIEW_STATE_MARKER_CHARS as C, MAX_PATCH_CHARS as Cn, MAX_UNIT_CANDIDATES as Ct, ReviewScopeMode as D, REVIEW_TOOL_RESULT_MAX_BYTES as Dn, ReviewWorkPhase as Dt, ReviewMode as E, PullRequestReviewer as En, ReviewWorkPerspective as Et, buildProfileMission as F, ReviewVerdict as Fn, fileReviewDelegation as Ft, webCryptoReviewStateAuthenticatorLayer as G, resolveGuidance as Gn, MAX_REPORTED_UNASSIGNED_EVIDENCE_SHARDS as Gt, toStoredFinding as H, makeReviewInstructions as Hn, reviewCandidateSubjectKey as Ht, computeProfileFingerprint as I, WalkthroughEntry as In, fileReviewPolicy as It, extractFingerprint as J, MAX_FILE_CHARS as Jn, MAX_UNIT_FILES as Jt, FINGERPRINT_MARKER_LENGTH as K, reviewInstructions as Kn, MAX_REVIEW_UNITS as Kt, fromStoredConcern as L, clampMaxFindings as Ln, fileReviewerInstructions as Lt, ReviewStateMarkerTooLarge as M, ReviewMission as Mn, fanOutHandlersLayer as Mt, StoredReviewConcern as N, ReviewToolkit as Nn, fanOutHandlersLayerFor as Nt, ReviewState as O, ReadFile as On, assessmentSettlesSuggestionExactly as Ot, StoredReviewFinding as P, ReviewToolkitLayer as Pn, fanOutReviewInstructions as Pt, DelegateFileReview as Q, ReviewInputViolation as Qn, ReviewEvidenceShardId as Qt, fromStoredFinding as R, defaultReviewPolicy as Rn, makeFanOutReviewInstructions as Rt, GitCommitSha as S, MAX_FINDINGS as Sn, MAX_REVIEW_CHILDREN as St, ReviewHeadComparison as T, MAX_WALKTHROUGH_SUMMARY_CHARS as Tn, ReviewCandidateId as Tt, unavailableReviewStateAuthenticatorLayer as U, readFileDiffHandler as Un, MAX_FILE_EVIDENCE_CHARS as Ut, toStoredConcern as V, listChangedFilesHandler as Vn, mapFileReviewChildFailure as Vt, validateReviewState as W, readFileHandler as Wn, MAX_MERGED_FINDINGS as Wt, CandidateAssessment as X, PullRequestSource as Xn, ReviewDiscoveryPerspective as Xt, renderFingerprintMarker as Y, PullRequestMetadata as Yn, ReviewDiscoveryPass as Yt, ConcernCandidate as Z, PullRequestSourceFailure as Zn, ReviewEvidenceShard as Zt, ReviewRetirementHost as _, FindingCategory as _n, ListReviewUnits as _t, PriorReviews as a, UNIT_EVIDENCE_CHAR_BUDGET as an, annotatePatch as ar, FileReviewDelegationFailure as at, hasReviewMetadataMarker as b, ListChangedFilesQuery as bn, MAX_CHILD_FINDINGS as bt, fingerprintUnchanged as c, planReviewUnits as cn, isReviewableFile as cr, FileReviewReport as ct, gitHubReviewPublisherLayer as d, ChangedFilesView as dn, FileReviewToolkitLayer as dt, ReviewRiskCategory as en, anchorViolation as er, FanOutCoordinatorToolkit as et, gitHubReviewRetirementHostLayer as f, CodeReview as fn, FileReviewUnitFailed as ft, ReviewRetirementFailure as g, FileSliceQuery as gn, FindingCandidate as gt, RetirableReviewComment as h, FileSlice as hn, FileReviewer as ht, PriorReviewLookupFailure as i, UNIT_CHANGED_LINE_BUDGET as in, MAX_REVIEW_CONTENT_CHARS as ir, FileReviewBrief as it, ReviewStateMarker as j, ReviewFinding as jn, defaultFileReviewerPolicy as jt, ReviewStateAuthenticationFailure as k, ReadFileDiff as kn, confirmedFindingForPublication as kt, gitHubPriorReviewsLayer as l, rankAndDedupeFindings as ln, parsePatch as lr, FileReviewRequest as lt, RetirableReview as m, FileDiffView as mn, FileReviewWorkRejected as mt, GitHubApiFailure as n, ReviewUnitId as nn, ChangedFileStatus as nr, FanOutReviewToolkit as nt, PublishedReview as o, classifyReviewRisks as on, commentableLines as or, FileReviewEvidence as ot, parseGitHubSubmittedAt as p, FileDiffQuery as pn, FileReviewUnitResult as pt, computeChangesetFingerprint as q, MAX_CHANGED_FILES as qn, MAX_UNIT_EVIDENCE_SHARDS as qt, GitHubReviewTarget as r, ReviewUnitPlan as rn, ChangedPath as rr, FanOutReviewer as rt, ReviewPublisher as s, findingAnchorInUnitEvidence as sn, hasReviewableContent as sr, FileReviewFailure as st, DEFAULT_GITHUB_REVIEW_AUTHOR_LOGIN as t, ReviewUnit as tn, ChangedFile as tr, FanOutCoordinatorToolkitLayer as tt, gitHubPullRequestSourceLayer as u, ChangedFileSummary as un, renderReviewContent as ur, FileReviewToolkit as ut, ReviewRetirementReport as v, FindingSeverity as vn, ListReviewUnitsQuery as vt, ReviewExecutionContext as w, MAX_WALKTHROUGH_ENTRIES as wn, ReviewCandidate as wt, retireStaleReviews as x, MAX_CONCERNS as xn, MAX_FILE_REVIEW_TOOL_CALLS as xt, decideReviewRetirement as y, ListChangedFiles as yn, MAX_CHILD_CONCERNS as yt, selectReviewRange as z, fileDiffView as zn, makeFanOutReviewSuite as zt };
2617
+ export { DiscoveredConcern as $, commentableLines as $n, boundedListReason as $t, ReviewStateAuthenticationFailure as A, clampMaxFindings as An, MAX_UNIT_FILES as At, selectReviewRange as B, MAX_CHANGED_FILES as Bn, UNIT_CHANGED_LINE_BUDGET as Bt, MAX_REVIEW_STATE_MARKER_CHARS as C, ReviewConcern as Cn, reviewCandidateSubjectKey as Ct, ReviewMode as D, ReviewToolkitLayer as Dn, MAX_REPORTED_UNASSIGNED_EVIDENCE_SHARDS as Dt, ReviewHeadComparison as E, ReviewToolkit as En, MAX_MERGED_FINDINGS as Et, StoredReviewFinding as F, makeReviewInstructions as Fn, ReviewPassId as Ft, validateReviewState as G, ReviewInputViolation as Gn, rankAndDedupeConcerns as Gt, toStoredConcern as H, PullRequestMetadata as Hn, classifyReviewRisks as Ht, buildProfileMission as I, readFileDiffHandler as In, ReviewRiskCategory as It, computeChangesetFingerprint as J, ChangedFile as Jn, FailedReviewUnit as Jt, webCryptoReviewStateAuthenticatorLayer as K, normalizeRepoRelativePath as Kn, rankAndDedupeFindings as Kt, computeProfileFingerprint as L, readFileHandler as Ln, ReviewUnit as Lt, ReviewStateMarker as M, fileDiffView as Mn, ReviewDiscoveryPerspective as Mt, ReviewStateMarkerTooLarge as N, fileReviewEvidenceChunks as Nn, ReviewEvidenceShard as Nt, ReviewScopeMode as O, ReviewVerdict as On, MAX_REVIEW_UNITS as Ot, StoredReviewConcern as P, listChangedFilesHandler as Pn, ReviewEvidenceShardId as Pt, ConcernCandidate as Q, annotatePatch as Qn, assessFlatReview as Qt, fromStoredConcern as R, resolveGuidance as Rn, ReviewUnitId as Rt, GitCommitSha as S, ReadFileDiff as Sn, makeFileReviewerInstructions as St, ReviewExecutionContext as T, ReviewMission as Tn, MAX_FILE_EVIDENCE_CHARS as Tt, toStoredFinding as U, PullRequestSource as Un, findingAnchorInUnitEvidence as Ut, selectedPullRequestSourceLayer as V, MAX_FILE_CHARS as Vn, UNIT_EVIDENCE_CHAR_BUDGET as Vt, unavailableReviewStateAuthenticatorLayer as W, PullRequestSourceFailure as Wn, planReviewUnits as Wt, renderFingerprintMarker as X, ChangedPath as Xn, ReviewCoverage as Xt, extractFingerprint as Y, ChangedFileStatus as Yn, ReviewAssurance as Yt, CandidateAssessment as Z, MAX_REVIEW_CONTENT_CHARS as Zn, ReviewInputCoverage as Zt, ReviewRetirementHost as _, MAX_WALKTHROUGH_ENTRIES as _n, assessmentSettlesSuggestionExactly as _t, PriorReviews as a, CodeReview as an, FindingCandidate as at, hasReviewMetadataMarker as b, REVIEW_TOOL_RESULT_MAX_BYTES as bn, fileReviewerInstructions as bt, fingerprintUnchanged as c, FileSlice as cn, MAX_FILE_REVIEW_TOOL_CALLS as ct, gitHubReviewPublisherLayer as d, FindingSeverity as dn, REVIEW_UNIT_CONCURRENCY as dt, compatibilityCoverage as en, hasReviewableContent as er, FileReviewBrief as et, gitHubReviewRetirementHostLayer as f, ListChangedFiles as fn, ReviewCandidate as ft, ReviewRetirementFailure as g, MAX_PATCH_CHARS as gn, ReviewWorkPhase as gt, RetirableReviewComment as h, MAX_FINDINGS as hn, ReviewWorkPerspective as ht, PriorReviewLookupFailure as i, ChangedFilesView as in, FileReviewer as it, ReviewStateAuthenticator as j, defaultReviewPolicy as jn, ReviewDiscoveryPass as jt, ReviewState as k, WalkthroughEntry as kn, MAX_UNIT_EVIDENCE_SHARDS as kt, gitHubPriorReviewsLayer as l, FileSliceQuery as ln, MAX_REVIEW_CHILDREN as lt, RetirableReview as m, MAX_CONCERNS as mn, ReviewPassMisbehaved as mt, GitHubApiFailure as n, flatAssurance as nn, parsePatch as nr, FileReviewReport as nt, PublishedReview as o, FileDiffQuery as on, MAX_CHILD_CONCERNS as ot, parseGitHubSubmittedAt as p, ListChangedFilesQuery as pn, ReviewCandidateId as pt, FINGERPRINT_MARKER_LENGTH as q, anchorViolation as qn, FailedReviewPass as qt, GitHubReviewTarget as r, ChangedFileSummary as rn, renderReviewContent as rr, FileReviewToolkit as rt, ReviewPublisher as s, FileDiffView as sn, MAX_CHILD_FINDINGS as st, DEFAULT_GITHUB_REVIEW_AUTHOR_LOGIN as t, fanOutInputCoverage as tn, isReviewableFile as tr, FileReviewEvidence as tt, gitHubPullRequestSourceLayer as u, FindingCategory as un, MAX_UNIT_CANDIDATES as ut, ReviewRetirementReport as v, MAX_WALKTHROUGH_SUMMARY_CHARS as vn, confirmedFindingForPublication as vt, MAX_STORED_UNREVIEWED_PATHS as w, ReviewFinding as wn, runFanOutReview as wt, retireStaleReviews as x, ReadFile as xn, makeFileReviewerDefinition as xt, decideReviewRetirement as y, PullRequestReviewer as yn, defaultFileReviewerPolicy as yt, fromStoredFinding as z, reviewInstructions as zn, ReviewUnitPlan as zt };
2302
2618
 
2303
- //# sourceMappingURL=github-BgtP7Rdv.mjs.map
2619
+ //# sourceMappingURL=github-BbwYzNrC.mjs.map