Skip to content

fix: make reviewer coverage resilient (exempt lockfiles; degrade over-long constraints) - #567

Merged
piekstra merged 4 commits into
mainfrom
exempt-lockfiles-from-coverage
Aug 18, 2026
Merged

fix: make reviewer coverage resilient (exempt lockfiles; degrade over-long constraints)#567
piekstra merged 4 commits into
mainfrom
exempt-lockfiles-from-coverage

Conversation

@piekstra

Copy link
Copy Markdown
Contributor

Problem

A reviewer that skips a dependency lockfile (Cargo.lock, package-lock.json, go.sum, …) is marked incomplete_skipped, and hasIncompleteReviewerCoverage then downgrades an otherwise-clean APPROVE to a COMMENT. Because a reviewer will always skip a machine-generated lockfile (there's nothing to read line by line), this permanently blocks approval on any PR where a churned lockfile is the only "unreviewed" file — e.g. a Cargo.lock regenerated by a dependency-version bump on an otherwise clean PR. Re-running never clears it.

Fix

Exempt generated lockfiles from the reviewer coverage universe in buildReviewerCoverage:

  • changedFiles is filtered before coverage is computed, so an unassigned lockfile can't become incomplete_unassigned.
  • each agent's scope is filtered too, so a lockfile explicitly assigned to an agent and skipped can't become incomplete_skipped.

Lockfiles are reviewed (if at all) through the manifest change that produced them, never line by line, so leaving them out of the coverage obligation is correct — not a loophole. isGeneratedLockfile matches by basename against the well-known set (Cargo, npm/yarn/pnpm/bun, Go, Bundler, Poetry/Pipenv, Composer, CocoaPods, Nix, Mix).

Tests

New TestBuildReviewerCoverageExemptsGeneratedLockfiles: a reviewer that inspects the real change and skips only Cargo.lock is complete_constrained (not incomplete_skipped), and an unassigned yarn.lock produces no coverage row. go test ./internal/pipeline/ ./internal/reviewplan/, go vet, gofmt all clean.

Found while landing an unrelated PR whose Cargo.lock (bumped by a dep upgrade) was the only file cr wouldn't sign off on.

A reviewer that skips a dependency lockfile (Cargo.lock, package-lock.json,
go.sum, …) was marked incomplete_skipped, and hasIncompleteReviewerCoverage
then downgraded an otherwise-clean APPROVE to a COMMENT — permanently, since
the reviewer will always skip a machine-generated lockfile. Exempt lockfiles
from the coverage universe (they are reviewed, if at all, via the manifest
change that produced them, never line by line) so neither a skipped nor an
unassigned lockfile blocks approval.

Seen in the wild: a Cargo.lock churned by a v0.4→v0.5 dependency bump was the
only 'unreviewed' file on a clean PR, and cr would not approve it.
… failing the reviewer

A reviewer whose coverage `constraints` entry exceeded 300 runes (or exceeded
the count cap, or was empty/duplicate) failed DecodeFindings with 'entry length
out of bounds', which surfaced downstream as 'completed without a result file'
and sank the whole reviewer — blocking approval on an otherwise-clean review. A
single legitimate ~300-rune note ('could not verify against source-of-truth
docs not in context') did exactly that, repeatably.

Coverage constraints are informational, not a contract: cap the count, truncate
an over-long entry, and drop empties/duplicates rather than erroring. Tests
updated to assert graceful degradation.
@piekstra piekstra changed the title fix(pipeline): exempt generated lockfiles from reviewer coverage fix: make reviewer coverage resilient (exempt lockfiles; degrade over-long constraints) Aug 13, 2026

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: 6d0772e32c08
Profile: reviewer - Posting as: piekstra-dev

Summary

Reviewer Findings
go:implementation-tests 1
structure:repo-health 0
harness-engineering:repo-health 1
architecture:solid 2
go:implementation-tests (1 finding)

Minor - internal/llm/contracts.go:367

decodeCoverageStrings truncates an over-long constraint to defaultMaxCoverageConstraintRunes (300) and then appends "..." via truncateRunes, so the value that survives sanitize() can be up to 303 runes — 3 runes past the documented/enforced cap (DefaultFindingsConstraintLimits().MaxRunesPerEntry = 300, which the orchestrator prompt tells reviewers is the hard limit). TestDecodeFindingsConstraintRuneBoundaries only asserts the result ends with "...", not that it respects the rune cap, so this drift isn't caught. Fix by reserving the ellipsis width before truncating, e.g. truncateRunes(value, defaultMaxCoverageConstraintRunes-3), or by post-truncating the whole result (including the suffix) down to defaultMaxCoverageConstraintRunes.

harness-engineering:repo-health (1 finding)

Major - internal/pipeline/pipeline.go:2437

docs/checkout-native-review-contract.md is the documented source of truth for reviewer coverage semantics: it defines "readable files" as "all changed files in the workbench" and states unassigned/skipped files "must not turn into a clean approval silently." This diff's buildReviewerCoverage now filters generated lockfiles out of changedFiles (and each agent's scope) before computing coverage, so lockfiles are silently excluded from the readable-files universe the doc describes — but the doc's coverage-status section (lines ~337-354) was not updated to mention the lockfile exemption or the new filterReviewableFiles/isGeneratedLockfile step. A future agent or engineer reading that doc to understand what "complete" coverage guarantees will get a materially misleading picture of what the harness actually enforces. Update docs/checkout-native-review-contract.md to document the lockfile exemption alongside the existing coverage-status definitions.

architecture:solid (2 findings)

Minor - internal/pipeline/pipeline.go:2489

U-S1: the exemption is applied at the accounting layer only, so the repo now answers "what is in the coverage universe?" in two places that disagree. ensureSelectedGlobCoverage (pipeline.go:1561-1594) still iterates the unfiltered changedFiles and force-assigns an uncovered lockfile to the first glob-matching agent, widening that agent's AllowedFiles/Files. The reviewer is therefore still told to cover Cargo.lock in its prompt scope, and the obligation is then silently discarded here. A smaller observable version of the same split: entry.Scope is filtered (line 2503) but entry.InspectedFiles (line 2524) is not, so a reviewer that did read a lockfile emits a coverage row listing an inspected file that is not in its scope.

Choosing the accounting layer is the right call — filtering prepared.changedFiles wholesale would also drop lockfiles from FindingsOptions.ChangedFiles and make a legitimate finding on a swapped registry URL undecodable — so the fix is not to move the filter but to give the universe one owner. Concretely: export the predicate as a single helper used both here and in ensureSelectedGlobCoverage's covered/assignment loops (e.g. have that function skip files where isGeneratedLockfile(file) before looking for an owner), and apply the same filter to entry.InspectedFiles so scope and coverage rows are drawn from one set. Without that, the next place that computes an obligation from raw changedFiles reintroduces the stall this PR fixes.

Nits - internal/llm/contracts.go:359

U-L2 (naming, not behavior): the degrade-don't-reject decision is sound and explicitly commented, so the swallowed-error rule is satisfied. Two labels are now stale around it. decodeCoverageStrings sits next to decodeCoverageFiles (line 335) with a near-identical name and the opposite error contract — one rejects, one silently repairs; decodeCoverageConstraints would carry the distinction, since constraints are its only caller (line 275). And DefaultFindingsConstraintLimits's doc comment still reads "limits enforced by DecodeFindings" (line 41), which now overstates: entries are capped and truncated, never enforced by failure. Prompt text at prompts.go:716-717 can keep saying "must" — instructing the model harder than the validator fails is deliberate.

Reviewer Coverage

  • go:implementation-tests — complete (broad); skipped: none; constraints: none
  • structure:repo-health — complete (broad); inspected 2 assigned files (4 inspected across reviewers): internal/pipeline/pipeline.go, internal/pipeline/pipeline_test.go; skipped: none; constraints: Could not run git diff between base and head SHAs (git commands required interactive approval unavailable in this session); reviewed the head-checkout source directly instead of the exact unified diff.
  • harness-engineering:repo-health — complete (broad); inspected 2 assigned files (4 inspected across reviewers): internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: none
  • architecture:solid — complete (broad); inspected 2 assigned files (4 inspected across reviewers): internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: Reviewed only the two assigned files; contracts_test.go and pipeline_test.go were read for intent but not reviewed as changed files. Sandbox blocked git in the reviewer checkout, so the diff was reconstructed from the head-of-branch files plus the change map rather than read as a patch; changed-line boundaries in contracts.go are inferred. Sandbox blocked running the project's verification (go test ./internal/llm/ ./internal/pipeline/, go vet); findings rest on reading, not on a reproduced build or test run.
Inspected files (4)
  • internal/llm/contracts.go
  • internal/llm/contracts_test.go
  • internal/pipeline/pipeline.go
  • internal/pipeline/pipeline_test.go

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 6m 07s | $4.91 | claude-sonnet-5, claude-opus-5 | cr dev
Field Value
Model claude-sonnet-5, claude-opus-5
Reviewers go:implementation-tests, structure:repo-health, harness-engineering:repo-health, architecture:solid
Engine claude_cli · claude-sonnet-5, claude-opus-5
Reviewed by cr · piekstra-dev
Duration 6m 07s wall · 13m 14s compute
Cost $4.91
Tokens 208 in / 54.6k out

Per-workstream usage

  • orchestrator-selection — claude-sonnet-5
    • In: 6
    • Out: 2.8k
    • Cache read: 66.9k
    • Cache create: 18.1k
    • Cost: $0.17
    • Duration: 30s
  • go:implementation-tests — claude-sonnet-5
    • In: 48
    • Out: 12.4k
    • Cache read: 1.3M
    • Cache create: 64.6k
    • Cost: $0.96
    • Duration: 3m 02s
  • structure:repo-health — claude-sonnet-5
    • In: 58
    • Out: 12.5k
    • Cache read: 1.3M
    • Cache create: 56.5k
    • Cost: $0.91
    • Duration: 3m 01s
  • harness-engineering:repo-health — claude-sonnet-5
    • In: 28
    • Out: 6.2k
    • Cache read: 680.9k
    • Cache create: 57.1k
    • Cost: $0.64
    • Duration: 1m 15s
  • architecture:solid — claude-opus-5
    • In: 62
    • Out: 19.9k
    • Cache read: 1.4M
    • Cache create: 80.9k
    • Cost: $2.00
    • Duration: 4m 49s
  • orchestrator-rollup — claude-sonnet-5
    • In: 6
    • Out: 882
    • Cache read: 100.4k
    • Cache create: 30.7k
    • Cost: $0.23
    • Duration: 36s

}
// Generated lockfiles are not a review obligation: exclude them so neither a
// reviewer that skips one nor an unassigned lockfile blocks approval.
changedFiles = filterReviewableFiles(changedFiles)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

U-S1: the exemption is applied at the accounting layer only, so the repo now answers "what is in the coverage universe?" in two places that disagree. ensureSelectedGlobCoverage (pipeline.go:1561-1594) still iterates the unfiltered changedFiles and force-assigns an uncovered lockfile to the first glob-matching agent, widening that agent's AllowedFiles/Files. The reviewer is therefore still told to cover Cargo.lock in its prompt scope, and the obligation is then silently discarded here. A smaller observable version of the same split: entry.Scope is filtered (line 2503) but entry.InspectedFiles (line 2524) is not, so a reviewer that did read a lockfile emits a coverage row listing an inspected file that is not in its scope.

Choosing the accounting layer is the right call — filtering prepared.changedFiles wholesale would also drop lockfiles from FindingsOptions.ChangedFiles and make a legitimate finding on a swapped registry URL undecodable — so the fix is not to move the filter but to give the universe one owner. Concretely: export the predicate as a single helper used both here and in ensureSelectedGlobCoverage's covered/assignment loops (e.g. have that function skip files where isGeneratedLockfile(file) before looking for an owner), and apply the same filter to entry.InspectedFiles so scope and coverage rows are drawn from one set. Without that, the next place that computes an obligation from raw changedFiles reintroduces the stall this PR fixes.

Reply inline to this comment.

Comment thread internal/llm/contracts.go Outdated
// rather than failing the decode. Failing here sinks the whole reviewer as
// "completed without a result file" and blocks approval on an otherwise-clean
// review, which a single legitimate ~300-rune constraint once did.
func decodeCoverageStrings(values []string) []string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

U-L2 (naming, not behavior): the degrade-don't-reject decision is sound and explicitly commented, so the swallowed-error rule is satisfied. Two labels are now stale around it. decodeCoverageStrings sits next to decodeCoverageFiles (line 335) with a near-identical name and the opposite error contract — one rejects, one silently repairs; decodeCoverageConstraints would carry the distinction, since constraints are its only caller (line 275). And DefaultFindingsConstraintLimits's doc comment still reads "limits enforced by DecodeFindings" (line 41), which now overstates: entries are capped and truncated, never enforced by failure. Prompt text at prompts.go:716-717 can keep saying "must" — instructing the model harder than the validator fails is deliberate.

Reply inline to this comment.

@@ -2437,10 +2437,56 @@ func reviewerToolEvidenceByAgent(sessions []sessionDraft) map[string]*llm.Review
return out

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

File-level note: internal/pipeline/pipeline.go

docs/checkout-native-review-contract.md is the documented source of truth for reviewer coverage semantics: it defines "readable files" as "all changed files in the workbench" and states unassigned/skipped files "must not turn into a clean approval silently." This diff's buildReviewerCoverage now filters generated lockfiles out of changedFiles (and each agent's scope) before computing coverage, so lockfiles are silently excluded from the readable-files universe the doc describes — but the doc's coverage-status section (lines ~337-354) was not updated to mention the lockfile exemption or the new filterReviewableFiles/isGeneratedLockfile step. A future agent or engineer reading that doc to understand what "complete" coverage guarantees will get a materially misleading picture of what the harness actually enforces. Update docs/checkout-native-review-contract.md to document the lockfile exemption alongside the existing coverage-status definitions.

Reply inline to this comment.

Comment thread internal/llm/contracts.go Outdated
for _, value := range values {
if utf8.RuneCountInString(value) > defaultMaxCoverageConstraintRunes {
return nil, fmt.Errorf("llm: %s entry length out of bounds", name)
value = truncateRunes(value, defaultMaxCoverageConstraintRunes)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

decodeCoverageStrings truncates an over-long constraint to defaultMaxCoverageConstraintRunes (300) and then appends "..." via truncateRunes, so the value that survives sanitize() can be up to 303 runes — 3 runes past the documented/enforced cap (DefaultFindingsConstraintLimits().MaxRunesPerEntry = 300, which the orchestrator prompt tells reviewers is the hard limit). TestDecodeFindingsConstraintRuneBoundaries only asserts the result ends with "...", not that it respects the rune cap, so this drift isn't caught. Fix by reserving the ellipsis width before truncating, e.g. truncateRunes(value, defaultMaxCoverageConstraintRunes-3), or by post-truncating the whole result (including the suffix) down to defaultMaxCoverageConstraintRunes.

Reply inline to this comment.

… length, docs

- ensureSelectedGlobCoverage skips generated lockfiles too, so a reviewer's
  prompt scope no longer lists a file the accounting layer then exempts; the
  coverage universe has one owner. Also filter entry.InspectedFiles so scope
  and coverage rows draw from the same set.
- decodeCoverageConstraints (renamed from decodeCoverageStrings): sanitize then
  clamp, so a truncated/marker-grown entry always fits the 300-rune cap
  (ellipsis reserved). Test asserts the rune cap, not just the ellipsis.
- DefaultFindingsConstraintLimits doc reworded (capped/truncated, not enforced
  by failure).
- docs/checkout-native-review-contract.md documents the lockfile exemption in
  the readable-files / coverage-status section.
@piekstra

Copy link
Copy Markdown
Contributor Author

Addressed all findings in the latest commit:

  • Major (doc): docs/checkout-native-review-contract.md now documents the lockfile exemption in the readable-files / coverage-status section.
  • Split universe (architecture): ensureSelectedGlobCoverage now skips lockfiles too, so a reviewer's prompt scope no longer lists a file the accounting layer exempts; also filtered entry.InspectedFiles so scope and coverage rows draw from one set.
  • Ellipsis cap (tests): sanitize-then-clamp with the ellipsis width reserved, so a truncated/marker-grown constraint always fits the 300-rune cap; the boundary test now asserts the rune count, not just the ... suffix.
  • Naming nit: decodeCoverageStringsdecodeCoverageConstraints; DefaultFindingsConstraintLimits doc reworded (capped/truncated, not enforced-by-failure).

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: 7157adbdc4f5
Profile: reviewer - Posting as: piekstra-dev

Summary

Reviewer Findings
go:implementation-tests 1
structure:repo-health 0
harness-engineering:repo-health 0
architecture:solid 1
go:implementation-tests (1 finding)

Major - internal/pipeline/pipeline.go:1582

This revision adds two new lockfile-exemption branches beyond the one covered by TestBuildReviewerCoverageExemptsGeneratedLockfiles, and neither has a regression test: (1) here in ensureSelectedGlobCoverage, an uncovered lockfile matching a full-scope agent's globs is now deliberately skipped instead of force-assigned — no test drives a changed-file set where a lockfile (e.g. Cargo.lock) matches an agent's FileGlobs, so a future edit that removes or reorders this continue would silently start force-assigning lockfiles into agent scope again, which is exactly the coverage regression this PR fixes. (2) in buildReviewerCoverage (pipeline.go:2533), entry.InspectedFiles = filterReviewableFiles(copySortedStrings(result.InspectedFiles)) strips a lockfile a reviewer explicitly reported inspecting, but TestBuildReviewerCoverageExemptsGeneratedLockfiles only exercises the skipped_files path, never a result with InspectedFiles containing a lockfile. Add a case to TestEnsureSelectedGlobCoverageAssignsUncoveredMatchingFiles (or a new test) with a lockfile in changed that matches an agent's glob, asserting it stays unassigned, and extend the lockfile-coverage test with a result.InspectedFiles entry for a lockfile, asserting it's dropped from the summary's InspectedFiles.

architecture:solid (1 finding)

Nits - internal/llm/contracts.go:373

U-S1: sanitize-before-clamp is the right order (marker.SanitizeModelContent replaces <!-- codereview: with a 3-rune-longer <!-- codereview:, so the old order could exceed the cap; truncation can only delete a suffix, so it cannot re-form a marker opening). The residue is that defaultMaxCoverageConstraintRunes-3 encodes a private detail of a helper in another file — truncateRunes appends a literal "..." (adapter.go:401) — so changing that ellipsis to "…" silently makes every caller that budgets for it wrong. The comment documents the coupling rather than removing it. Consider a sibling helper next to truncateRunes that takes a total budget (e.g. truncateRunesTotal(value, max) returning a string of at most max runes including the marker), leaving the arithmetic with the code that appends. Defensible as-is given the test at contracts_test.go:213-216 now asserts the ≤300 postcondition.

Reviewer Coverage

  • go:implementation-tests — complete (constrained); inspected 4 assigned files (5 inspected across reviewers): internal/llm/contracts.go, internal/llm/contracts_test.go, internal/pipeline/pipeline.go, internal/pipeline/pipeline_test.go; skipped: none; constraints: none
  • structure:repo-health — complete (constrained); inspected 2 assigned files (5 inspected across reviewers): internal/pipeline/pipeline.go, internal/pipeline/pipeline_test.go; skipped: none; constraints: Could not run git diff/log between base and head SHAs (git commands required interactive approval unavailable in this session); reviewed the head-checkout source directly instead of the exact unified diff.
  • harness-engineering:repo-health — complete (constrained); inspected 3 assigned files (5 inspected across reviewers): docs/checkout-native-review-contract.md, internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: none
  • architecture:solid — complete (constrained); inspected 2 assigned files (5 inspected across reviewers): internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: Reviewed only the two assigned Go files; docs/checkout-native-review-contract.md and the two _test.go files were read as context for intent and test coverage, not reviewed as changed files. Sandbox blocked git in the reviewer checkout, so the diff was reconstructed from head-of-branch files plus the change map rather than read as a patch; changed-line boundaries are inferred. Sandbox blocked running the project's verification (go test ./internal/llm/ ./internal/pipeline/, go vet); findings rest on reading, not on a reproduced build or test run.
Inspected files (5)
  • docs/checkout-native-review-contract.md
  • internal/llm/contracts.go
  • internal/llm/contracts_test.go
  • internal/pipeline/pipeline.go
  • internal/pipeline/pipeline_test.go

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 3m 29s | $5.23 | claude-sonnet-5, claude-opus-5 | cr dev
Field Value
Model claude-sonnet-5, claude-opus-5
Reviewers go:implementation-tests, structure:repo-health, harness-engineering:repo-health, architecture:solid
Engine claude_cli · claude-sonnet-5, claude-opus-5
Reviewed by cr · piekstra-dev
Duration 3m 29s wall · 6m 17s compute
Cost $5.23
Tokens 94 in / 24.8k out

Per-workstream usage

  • go:implementation-tests — claude-sonnet-5
    • In: 24
    • Out: 6.1k
    • Cache read: 1.1M
    • Cache create: 164.3k
    • Cost: $1.41
    • Duration: 1m 36s
  • structure:repo-health — claude-sonnet-5
    • In: 18
    • Out: 5.6k
    • Cache read: 707.5k
    • Cache create: 75.2k
    • Cost: $0.75
    • Duration: 1m 21s
  • harness-engineering:repo-health — claude-sonnet-5
    • In: 16
    • Out: 2.6k
    • Cache read: 611.7k
    • Cache create: 70.0k
    • Cost: $0.64
    • Duration: 37s
  • architecture:solid — claude-opus-5
    • In: 30
    • Out: 9.4k
    • Cache read: 1.4M
    • Cache create: 120.3k
    • Cost: $2.15
    • Duration: 2m 22s
  • orchestrator-rollup — claude-sonnet-5
    • In: 6
    • Out: 1.1k
    • Cache read: 113.0k
    • Cache create: 37.3k
    • Cost: $0.27
    • Duration: 19s

// Generated lockfiles are not a coverage obligation (see
// buildReviewerCoverage): don't force-assign one to an agent, or its
// prompt scope would list a file the accounting layer then exempts.
if isGeneratedLockfile(file) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This revision adds two new lockfile-exemption branches beyond the one covered by TestBuildReviewerCoverageExemptsGeneratedLockfiles, and neither has a regression test: (1) here in ensureSelectedGlobCoverage, an uncovered lockfile matching a full-scope agent's globs is now deliberately skipped instead of force-assigned — no test drives a changed-file set where a lockfile (e.g. Cargo.lock) matches an agent's FileGlobs, so a future edit that removes or reorders this continue would silently start force-assigning lockfiles into agent scope again, which is exactly the coverage regression this PR fixes. (2) in buildReviewerCoverage (pipeline.go:2533), entry.InspectedFiles = filterReviewableFiles(copySortedStrings(result.InspectedFiles)) strips a lockfile a reviewer explicitly reported inspecting, but TestBuildReviewerCoverageExemptsGeneratedLockfiles only exercises the skipped_files path, never a result with InspectedFiles containing a lockfile. Add a case to TestEnsureSelectedGlobCoverageAssignsUncoveredMatchingFiles (or a new test) with a lockfile in changed that matches an agent's glob, asserting it stays unassigned, and extend the lockfile-coverage test with a result.InspectedFiles entry for a lockfile, asserting it's dropped from the summary's InspectedFiles.

Reply inline to this comment.

Comment thread internal/llm/contracts.go
if strings.TrimSpace(value) == "" {
return nil, fmt.Errorf("llm: %s entries must be non-empty", name)
if utf8.RuneCountInString(value) > defaultMaxCoverageConstraintRunes {
value = truncateRunes(value, defaultMaxCoverageConstraintRunes-3)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

U-S1: sanitize-before-clamp is the right order (marker.SanitizeModelContent replaces <!-- codereview: with a 3-rune-longer <!-- codereview:, so the old order could exceed the cap; truncation can only delete a suffix, so it cannot re-form a marker opening). The residue is that defaultMaxCoverageConstraintRunes-3 encodes a private detail of a helper in another file — truncateRunes appends a literal "..." (adapter.go:401) — so changing that ellipsis to "…" silently makes every caller that budgets for it wrong. The comment documents the coupling rather than removing it. Consider a sibling helper next to truncateRunes that takes a total budget (e.g. truncateRunesTotal(value, max) returning a string of at most max runes including the marker), leaving the arithmetic with the code that appends. Defensible as-is given the test at contracts_test.go:213-216 now asserts the ≤300 postcondition.

Reply inline to this comment.

- TestEnsureSelectedGlobCoverageSkipsLockfiles: a lockfile matching an agent's
  globs stays unassigned instead of being force-assigned into scope.
- Extend the coverage test with a reviewer that reports inspecting Cargo.lock,
  asserting it's dropped from the summary's InspectedFiles.

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: 5fdbcebd8ad0
Profile: reviewer - Posting as: piekstra-dev

Summary

Reviewer Findings
go:implementation-tests 0
structure:repo-health 0
harness-engineering:repo-health 0
architecture:solid 0

Reviewer Coverage

  • go:implementation-tests — complete (constrained); inspected 4 assigned files (5 inspected across reviewers): internal/llm/contracts.go, internal/llm/contracts_test.go, internal/pipeline/pipeline.go, internal/pipeline/pipeline_test.go; skipped: none; constraints: none
  • structure:repo-health — complete (constrained); inspected 2 assigned files (5 inspected across reviewers): internal/pipeline/pipeline.go, internal/pipeline/pipeline_test.go; skipped: none; constraints: Could not run git diff/log between base and head SHAs (git commands required interactive approval unavailable in this session); reviewed the head-checkout source directly instead of the exact unified diff.
  • harness-engineering:repo-health — complete (constrained); inspected 3 assigned files (5 inspected across reviewers): docs/checkout-native-review-contract.md, internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: none
  • architecture:solid — complete (constrained); inspected 2 assigned files (5 inspected across reviewers): internal/llm/contracts.go, internal/pipeline/pipeline.go; skipped: none; constraints: Both assigned files are unchanged from the previously reviewed revision (same content at the same lines); the only delta at this head is +31 test lines in internal/pipeline/pipeline_test.go, which was read as context. Reviewed only the two assigned Go files; docs/checkout-native-review-contract.md and the two _test.go files were read for intent and coverage verification, not reviewed as changed files. Sandbox blocked git in the reviewer checkout, so the diff was reconstructed from head-of-branch files plus the change map rather than read as a patch; changed-line boundaries are inferred. Sandbox blocked running the project's verification (go test ./internal/llm/ ./internal/pipeline/, go vet); conclusions rest on reading, not on a reproduced build or test run. The one residual item on these files — defaultMaxCoverageConstraintRunes-3 encoding truncateRunes' literal "..." ellipsis at contracts.go:373 — is already carried by an open inline thread, so it was not re-filed as a duplicate finding.
Inspected files (5)
  • docs/checkout-native-review-contract.md
  • internal/llm/contracts.go
  • internal/llm/contracts_test.go
  • internal/pipeline/pipeline.go
  • internal/pipeline/pipeline_test.go

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 2m 04s | $4.52 | claude-sonnet-5, claude-opus-5 | cr dev
Field Value
Model claude-sonnet-5, claude-opus-5
Reviewers go:implementation-tests, structure:repo-health, harness-engineering:repo-health, architecture:solid
Engine claude_cli · claude-sonnet-5, claude-opus-5
Reviewed by cr · piekstra-dev
Duration 2m 04s wall · 3m 14s compute
Cost $4.52
Tokens 66 in / 11.7k out

Per-workstream usage

  • go:implementation-tests — claude-sonnet-5
    • In: 14
    • Out: 2.2k
    • Cache read: 777.3k
    • Cache create: 111.0k
    • Cost: $0.93
    • Duration: 42s
  • structure:repo-health — claude-sonnet-5
    • In: 14
    • Out: 2.4k
    • Cache read: 636.5k
    • Cache create: 88.9k
    • Cost: $0.76
    • Duration: 43s
  • harness-engineering:repo-health — claude-sonnet-5
    • In: 16
    • Out: 2.5k
    • Cache read: 706.1k
    • Cache create: 83.1k
    • Cost: $0.75
    • Duration: 34s
  • architecture:solid — claude-opus-5
    • In: 16
    • Out: 4.2k
    • Cache read: 921.3k
    • Cache create: 123.1k
    • Cost: $1.80
    • Duration: 1m 03s
  • orchestrator-rollup — claude-sonnet-5
    • In: 6
    • Out: 395
    • Cache read: 123.5k
    • Cache create: 40.6k
    • Cost: $0.29
    • Duration: 9s

@piekstra
piekstra merged commit 960b0fa into main Aug 18, 2026
10 checks passed
@piekstra
piekstra deleted the exempt-lockfiles-from-coverage branch August 18, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants