Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/llm-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,11 @@ jobs:
run: uv run python -m lean_pool.review

- name: Upload review evidence
if: success() && steps.ci.outputs.conclusion == 'success'
if: always() && steps.ci.outputs.conclusion == 'success'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: review-evidence
path: ${{ runner.temp }}/review-evidence.json
path: ${{ runner.temp }}/review-evidence*
Comment on lines +228 to +232

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 This changes the LLM review workflow’s artifact-upload condition and path. The repository directive prohibits changes to .github/workflows/ unless they were explicitly requested, so this requirement must be satisfied before merging.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

if-no-files-found: ignore
retention-days: 30

Expand Down
53 changes: 38 additions & 15 deletions python/lean_pool/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ class ReviewResult:
calls: int = 1
coverage: str | None = None
requests: tuple[ReviewResult, ...] = ()
source_reviews: tuple[ReviewResult, ...] = ()


def _review_messages(
Expand Down Expand Up @@ -1207,7 +1208,9 @@ def _review_in_portions(
manifest, [r.payload for r in results], shared
)
final = _integrate_portions(diff, evidence, obligations, budget, prepare, send)
return replace(final, portions=len(results), coverage=manifest)
return replace(
final, portions=len(results), coverage=manifest, source_reviews=tuple(results)
Comment on lines +1211 to +1212

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Portion Results Stored Twice

source_reviews retains the same source-portion results that _ReviewSession.accounted already stores in requests. Since _write_review_evidence recursively serializes both fields, large portioned reviews keep and write every source-review payload twice, unnecessarily increasing memory use and evidence-artifact size.

)


def _integrate_portions(
Expand All @@ -1233,13 +1236,11 @@ def _integrate_portions(
)
final = send(messages)
queries = final.payload.get("source_requests", [])
if (
not isinstance(queries, list)
or len(queries) > 20
or any(not isinstance(query, str) or not query.strip() for query in queries)
if not isinstance(queries, list) or any(
not isinstance(query, str) or not query.strip() for query in queries
):
raise ValueError(
"Integration source requests must be at most 20 nonempty strings"
f"Integration source requests must be nonempty strings: {queries!r}"
)
Comment on lines 1238 to 1244

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unbounded Source Retrieval

A model response with many source requests now passes validation. _fit_source_followups then scans the complete diff once for every request and allocates all excerpts before checking the candidate message against the input budget. A large request list can therefore exhaust review-worker CPU or memory and prevent the review from producing a verdict. Bound the retrieval work with the available budget before calling source_excerpts.

Knowledge Base Used: Fit oversized pull-request diffs to the review budget

if not queries:
break
Expand All @@ -1250,19 +1251,38 @@ def _integrate_portions(
)
)
bundle["obligations"] = obligations
updated = prepare(
json.dumps(bundle, ensure_ascii=False),
review_portions.integration_instructions(),
)
available = budget - _message_tokens(updated)
allowance = max(0, (available - 512) // len(queries))
bundle["source_followups"].extend(
review_portions.source_excerpts(diff, query, allowance) for query in queries
)
if not _fit_source_followups(diff, queries, bundle, budget, prepare):
break
payload = review_portions.enforce_resolutions(final.payload, obligations)
return replace(final, payload=payload)


def _fit_source_followups(
diff: str, queries: list[str], bundle: dict, budget: int, prepare: Callable
) -> bool:
"""Fit the serialized excerpts and their metadata within the next call."""
rules = review_portions.integration_instructions()
available = budget - _message_tokens(
prepare(json.dumps(bundle, ensure_ascii=False), rules)
)
allowance = max(
0, int((available - 512) * CHARS_PER_TOKEN_ESTIMATE) // len(queries)
)
for _ in range(20):
excerpts = [
review_portions.source_excerpts(diff, query, allowance) for query in queries
]
candidate = bundle | {"source_followups": bundle["source_followups"] + excerpts}
messages = prepare(json.dumps(candidate, ensure_ascii=False), rules)
if _message_tokens(messages) <= budget:
bundle["source_followups"] = candidate["source_followups"]
return True
if allowance == 0:
break
allowance //= 2
return False


class _ReviewSession:
"""Account for every call across concurrent portions and context retries."""

Expand All @@ -1279,6 +1299,9 @@ def send(self, messages: list[dict[str, str]]) -> ReviewResult:
result = _send_review(self.model, messages, self.effort)
with self.lock:
self.completed.append(result)
if destination := os.environ.get("REVIEW_EVIDENCE_PATH"):
with Path(destination).with_suffix(".calls.jsonl").open("a") as stream:
stream.write(json.dumps(asdict(result), default=vars) + "\n")
Comment on lines +1302 to +1304

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Checkpoint Errors Abort Reviews

If the artifact filesystem is full, unavailable, or misconfigured, this unguarded checkpoint write raises after the model call has succeeded. The filesystem error then aborts the entire review, so a best-effort diagnostic can discard a successful result and waste the completed model call. Handle checkpoint persistence failures separately from the review result.

Knowledge Base Used: Continuous integration and repository automation

return result

def accounted(self, result: ReviewResult) -> ReviewResult:
Expand Down
11 changes: 11 additions & 0 deletions python/lean_pool/review_portions.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,21 @@ def enforce_resolutions(payload: dict, obligations: dict[str, str]) -> dict:
if resolved - obligations.keys():
raise ValueError("Integration cites an unknown review obligation")
missing = obligations.keys() - resolved
final_concerns = {
identifier: concern
for identifier, concern in report_obligations(
"integration:final", payload
).items()
if not identifier.endswith(":verdict")
}
obligations = obligations | final_concerns
missing |= final_concerns.keys()
Comment thread
greptile-apps[bot] marked this conversation as resolved.
if missing:
payload = dict(payload)
findings = list(payload.get("findings") or [])
for identifier in sorted(missing):
if identifier.startswith("integration:final:finding:"):
continue # The concrete finding already appears in this final report.
findings.append(
{
"file": "",
Expand Down
114 changes: 114 additions & 0 deletions python/tests/test_review_portions.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,19 @@ def send(model, messages, effort):
"findings": [],
"open_questions": [],
"evidence_summary": "Reviewed unique_2_499 and local definitions.",
"source_number": int(
re.search(r"source portion (\d+)/", user).group(1)
),
}
)

monkeypatch.setattr(review, "_send_review", send)
answer = review.request_review(review.DEFAULT_MODEL, "rules", diff, "review")
assert answer.payload["verdict"] == "pass"
assert answer.portions > 1
assert [item.payload["source_number"] for item in answer.source_reviews] == list(
range(1, answer.portions + 1)
)
assert answer.calls == answer.portions + 1 == len(calls)
assert answer.usage.prompt_tokens == len(calls) * 10
assert answer.usage.completion_tokens == len(calls) * 3
Expand Down Expand Up @@ -496,3 +502,111 @@ def send(messages):
if not resolve:
assert "Potentially vacuous definition" in str(answer.payload["findings"])
assert "MissingDefinition" in str(answer.payload["findings"])


@pytest.mark.parametrize("field", ["findings", "open_questions", "source_requests"])
def test_final_integration_concerns_prevent_approval(field):
"""A final pass cannot override unresolved concerns in that same report."""
payload = {"verdict": "pass", field: ["Unresolved semantic question"]}
answer = review_portions.enforce_resolutions(payload, {})
assert answer["verdict"] == "discuss"
assert "Unresolved semantic question" in str(answer["findings"])


def test_source_excerpt_allowance_uses_characters(monkeypatch):
"""An excerpt can use the remaining token budget at the configured ratio."""
calls = []
allowances = []

def prepare(evidence, rules):
return [{"role": "user", "content": evidence}]

def send(messages):
calls.append(messages)
return result(
{"verdict": "pass", "source_requests": ["Known"] if len(calls) == 1 else []}
)

def excerpt(diff, query, allowance):
allowances.append(allowance)
return {"source": "def Known := 0"}

monkeypatch.setattr(review_portions, "source_excerpts", excerpt)
review._integrate_portions("def Known := 0", "{}", {}, 10_000, prepare, send)
assert 15_000 < allowances[0] < 20_000


def test_many_source_requests_are_not_discarded(monkeypatch):
"""The input budget, rather than an arbitrary request count, bounds retrieval."""
requested = [f"Definition{index}" for index in range(25)]
retrieved = []
calls = []

def prepare(evidence, rules):
return [{"role": "user", "content": evidence}]

def send(messages):
calls.append(messages)
return result(
{"verdict": "pass", "source_requests": requested if len(calls) == 1 else []}
)

def excerpt(diff, query, allowance):
retrieved.append(query)
return {"query": query, "status": "No matching source"}

monkeypatch.setattr(review_portions, "source_excerpts", excerpt)
answer = review._integrate_portions("", "{}", {}, 10_000, prepare, send)
assert retrieved == requested
assert answer.payload["verdict"] == "discuss"


def test_completed_calls_are_saved_before_later_failure(monkeypatch, tmp_path):
"""Successful source work survives an integration or transport failure."""
destination = tmp_path / "review-evidence.json"
monkeypatch.setenv("REVIEW_EVIDENCE_PATH", str(destination))
monkeypatch.setattr(
review, "_send_review", lambda *args: result({"verdict": "pass"})
)
session = review._ReviewSession(review.DEFAULT_MODEL, "xhigh")
session.send([])
saved = json.loads(destination.with_suffix(".calls.jsonl").read_text())
assert saved["payload"]["verdict"] == "pass"


def test_followup_budget_includes_serialized_metadata_and_escaping(monkeypatch):
"""Escaping and many per-query envelopes cannot overflow the next call."""
calls = []

def prepare(evidence, rules):
return [{"role": "user", "content": evidence}]

def send(messages):
assert review._message_tokens(messages) <= 10_000
calls.append(messages)
return result(
{
"verdict": "pass",
"source_requests": [f"D{i}" for i in range(20)]
if len(calls) == 1
else [],
}
)

def excerpt(diff, query, allowance):
return {"query": query, "source": "\\" * allowance, "status": "metadata " * 50}

monkeypatch.setattr(review_portions, "source_excerpts", excerpt)
answer = review._integrate_portions("", "{}", {}, 10_000, prepare, send)
assert len(calls) == 2
assert answer.payload["verdict"] == "discuss"


def test_final_findings_are_reported_once():
"""A retained finding blocks approval without a duplicate synthetic entry."""
finding = {"comment": "Unresolved semantic concern"}
answer = review_portions.enforce_resolutions(
{"verdict": "pass", "findings": [finding]}, {}
)
assert answer["verdict"] == "discuss"
assert answer["findings"] == [finding]
Loading