fix(review): scope discard rules gate on cause, not anchored file (trail finding) · Entire

fix(review): scope discard rules gate on cause, not anchored file (trail finding)

9198464·

peyton-alt·1w ago·4 files·+35 added/-2 removed

The absolute discard order ('findings that point at files not listed are out of scope') dropped a legitimate finding class: an in-scope change that breaks an UNCHANGED caller or consumer in an unlisted file — a cross-file regression, exactly what reviews exist to surface — got discarded because the finding anchors at the broken call site. Both the worker prompt's rule and the judge's scope gate now gate on where the CAUSE lives and explicitly keep in-scope-cause/out-of-scope-impact findings, anchored to the in-scope change.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Sessions

01KX18CRN7KXZZ55BS5R6WCMQAView transcript

[?
test(review): pin that codex $name skills survive the legacy repair + native invocationClaude Code·6 steps](/content/gh/entireio/cli/session/99f0d90a-6e31-4c60-ba62-aa2dccd29033#timeline-01KX18CRN7KXZZ55BS5R6WCMQA/index.html)

Changes

4

122 unmodified lines

123
124
125
126
126
127
128
129

122 unmodified lines

case listsTruncated:
        b.WriteString("\n\nThe file lists above are truncated. Prefer findings in the listed files; verify any finding outside them against `git diff` before keeping it.")
    default:
        b.WriteString("\n\nOnly the files listed above are in scope. Findings that point anywhere else are out of scope — discard them.")
        b.WriteString("\n\nOnly the files listed above are in scope. Findings caused by code outside them are out of scope — discard them. A listed file's change that breaks code in an unlisted file (an unchanged caller or consumer) IS in scope — report it anchored to the in-scope cause.")
    }

switch {

Mcmd/entire/cli/review/prompt.go+1/-1

380 unmodified lines

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

380 unmodified lines

t.Errorf("fenced block should be labeled as data, not instructions:\n%s", out)
    }
}
// TestRenderScopeContext_DiscardRuleIsCauseBased mirrors the judge-side pin
// for the worker prompt: the scope gate must not order reviewers to discard
// cross-file regressions (in-scope cause, out-of-scope impact).
func TestRenderScopeContext_DiscardRuleIsCauseBased(t *testing.T) {
t.Parallel()
out := renderScopeContext(reviewtypes.ScopeContext{Files: []string{"A\tchanged.go"}}, "main")
if !strings.Contains(out, "caused by") {
t.Errorf("worker discard rule should gate on cause:\n%s", out)
}
if !strings.Contains(out, "unlisted file") {
t.Errorf("worker discard rule should keep in-scope-cause/out-of-scope-impact findings:\n%s", out)
}
}

Mcmd/entire/cli/review/prompt_test.go+14

125 unmodified lines

126
127
128
129
129
130
131
132

125 unmodified lines

b.WriteString(fence + "scope\n")
    b.WriteString(data.String())
    b.WriteString(fence + "\n")
    note := "Findings that point at files not listed above are out of scope — discard them, no matter which reviewer reported them."
note := "Findings caused by code outside the files listed above are out of scope — discard them, no matter which reviewer reported them. Exception: keep a finding whose cause is a listed file even when its impact lands in an unlisted file (an in-scope change breaking an unchanged caller or consumer is in scope)."
if scope.FilesTruncated || scope.UncommittedTruncated {
    note = "This list is truncated. Prefer findings in the listed files; verify any finding outside them against `git diff` before keeping it."
}

Mcmd/entire/cli/review/synthesis_prompt.go+1/-1

43 unmodified lines

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

43 unmodified lines

t.Errorf("data block must be labeled untrusted:\n%s", out)
    }
}
// TestWriteSynthesisScopeGate_DiscardRuleIsCauseBased pins the discard
// rule's semantics: it must gate on where a finding's CAUSE lives, and
// explicitly keep cross-file regressions — an in-scope change breaking an
// unchanged caller in an unlisted file is exactly what reviews exist to
// catch, and a categorical anchored-file discard was dropping that class.
func TestWriteSynthesisScopeGate_DiscardRuleIsCauseBased(t *testing.T) {
t.Parallel()
var b strings.Builder
writeSynthesisScopeGate(&b, reviewtypes.ScopeContext{Files: []string{"A\tchanged.go"}})
out := b.String()

if !strings.Contains(out, "caused by") {
t.Errorf("discard rule should gate on the finding's cause, not its anchored file:\n%s", out)
}
if !strings.Contains(out, "unlisted file") {
t.Errorf("discard rule should keep in-scope-cause findings whose impact lands in an unlisted file:\n%s", out)
}
}

Mcmd/entire/cli/review/synthesis_prompt_internal_test.go+19