Skip to content

feat(foreman): wire the cross-stage contradiction detector into the reviewer path - #1675

Open
Defilan wants to merge 1 commit into
defilantech:mainfrom
Defilan:foreman/wl-1549-cross-stage-contradiction/issue-1549
Open

feat(foreman): wire the cross-stage contradiction detector into the reviewer path#1675
Defilan wants to merge 1 commit into
defilantech:mainfrom
Defilan:foreman/wl-1549-cross-stage-contradiction/issue-1549

Conversation

@Defilan

@Defilan Defilan commented Aug 26, 2026

Copy link
Copy Markdown
Member

What

Wires the cross-stage contradiction detector (pkg/foreman/agent/cross_stage.go)
into the reviewer path and deletes its dead-code suppression.

This is the first slice of #1549, not the whole of it. See Scope below — the
coder and gate stages are follow-up work tracked in #1674.

Why

cross_stage.go held a unit-tested detector with zero production callers,
suppressed in .golangci-deadcode.yml so CI stayed green while the feature never
ran.

Refs #1549

How

applyCrossStageContradictionsForTask builds a reviewer StageClaim from the
terminal summary and envelope, resolves ground-truth BranchFacts from the
branch the executor already cut, and runs the existing contradictions()
detector. Contradictions are logged and recorded on
Extra["crossStageContradictions"]. Records-and-logs: the reviewer's verdict is
left untouched.

It sits beside the existing rails in runLLMPath as a small apply*ForTask
wrapper, matching applyCoderGroundingRailForTask and
applyIssueClauseCoverageForTask, so that function's complexity budget is
unchanged.

Scope — what this does NOT do

contradictions() implements four rules. This wiring makes two of them
reachable, because it builds only a reviewer claim:

Rule Reachable here Why not
1. claims edits on an empty branch no ClaimsEdits is never set on a reviewer claim
2. claims empty branch when it is not yes
3. names a file not among the changed files yes
4. GATE-PASS on an empty branch no this call site only passes a reviewer verdict

Rules 1 and 4 are the two halves of the incident that motivated #1549 (a coder
claiming an edit on a branch with zero commits, then a trivially-passing gate).
They need coder and gate claims, which is #1674. Calling this "wires #1549" would
overclaim, hence Refs, not Fixes.

Also deferred to #1674: the issue asks to "mark the task for escalation" and
preserve both claims and the ground facts. This preserves the contradiction
strings only, and nothing escalates.

Verification

  • Mutation check: deleting the applyCrossStageContradictionsForTask call
    site makes TestCrossStageContradiction_WiredIntoRunLLMPath FAIL. The test
    constrains the wiring, not just the detector.
  • make lint-deadcode passes with 0 issues and the cross_stage.go
    suppression deleted — no //nolint, no fake reference. shouldEscalate is a
    real production caller (cross_stage_wiring.go:92), not a test-only one.
  • go test ./pkg/foreman/agent/... -count=1 green; build, vet, gofmt clean.
  • Rebased onto current main (was 9 commits behind). Two conflicts, both
    "two rails added in the same place": the .golangci-deadcode.yml resolution
    removes cross_stage.go while keeping the three entries already deleted on
    main deleted, and executor_native.go keeps both the [FEATURE] Require per-clause coverage of the issue's stated expected behaviour #1554 clause-coverage
    rail and this one.

Checklist

  • Tests added/updated
  • make test passes locally
  • make lint passes locally
  • Commit messages follow conventional commits
  • All commits are signed off (git commit -s) per DCO
  • AI assistance (if any) is disclosed above, per CONTRIBUTING.md
  • Documentation updated (if user-facing change) — internal rail, no user-facing surface

AI assistance

The implementation commit was authored by a Foreman coder agent
(Ornith-1.5-35B-A3B, in-cluster). The rebase, conflict resolution, adversarial
review and scoping above were done with Claude Code. A human owns this review
conversation.

…eviewer path (defilantech#1549)

The deterministic contradiction detector in cross_stage.go
(contradictions + shouldEscalate) was merged and unit-tested but had no
production caller; a .golangci-deadcode suppression kept CI green while the
feature never ran. Wire it into the reviewer path of runLLMPath, where it can
actually fire and where issue defilantech#1549's second incident landed: a reviewer
asserting the branch is empty while the ground-truth diff is non-empty.

The new applyCrossStageContradictionsForTask wrapper mirrors the sibling rails
(applyCoderGroundingRailForTask, applyNoFunctionalChangeForTask): it builds a
StageClaim from the reviewer's verdict + emptiness prose + named files and a
BranchFacts from the reviewBase/reviewDiff already in scope, records every
contradiction on the terminal result under crossStageContradictions, and logs
each one. It is non-blocking: it never changes the verdict and degrades open
on a missing diff.

Also delete the cross_stage.go entry from .golangci-deadcode.yml, the proof
the rail is now live: make lint-deadcode passes with the suppression gone.

Add one production-path test (drives Execute -> runLLMPath -> the reviewer
block) that asserts the contradiction is recorded on a non-empty branch whose
reviewer claims it is empty. Verified non-vacuous via the mutation check:
commenting out the call site makes the test fail.

Refs defilantech#1549

Signed-off-by: Foreman Bot <chris@mahercode.io>
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/foreman/agent/cross_stage_wiring.go 78.78% 4 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

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.

1 participant