gitbutlerapp/gitbutler · error

candidate turns always have review or branch evidence

Error message

candidate turns always have review or branch evidence

What it means

`evidence_tier` in but-agentlog's projection maps `(has_review, has_branch)` to a `ProjectionEvidenceTier`; the `(false, false)` arm is `unreachable!` because upstream candidate collection only keeps turns that already carry review or branch evidence. The panic therefore signals that the candidate filter upstream of this function was bypassed or regressed, letting evidence-free turns through.

Source

Thrown at crates/but-agentlog/src/projection.rs:544

    if has_review {
        reasons.push(ProjectionMatchReason {
            kind: ProjectionMatchKind::ReviewTarget,
        });
    }
    if has_branch {
        reasons.push(ProjectionMatchReason {
            kind: ProjectionMatchKind::BranchTarget,
        });
    }
    reasons
}

fn evidence_tier(has_review: bool, has_branch: bool) -> ProjectionEvidenceTier {
    match (has_review, has_branch) {
        (true, true) => ProjectionEvidenceTier::Supporting,
        (true, false) => ProjectionEvidenceTier::Direct,
        (false, true) => ProjectionEvidenceTier::Possible,
        (false, false) => unreachable!("candidate turns always have review or branch evidence"),
    }
}

fn evidence_rank(tier: ProjectionEvidenceTier) -> usize {
    match tier {
        ProjectionEvidenceTier::Supporting => 0,
        ProjectionEvidenceTier::Direct => 1,
        ProjectionEvidenceTier::Possible => 2,
    }
}

fn project_records(
    request: &ProjectionRequest,
    session_key: &str,
    turn_key: &str,
    records: SessionRecords,
    saw_truncated_text: &mut bool,
) -> Vec<ProjectionRecord> {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Filter candidates to `has_review || has_branch` before computing tiers.
  2. If new legitimate turn kinds can be evidence-free, change `evidence_tier` to return `Option<ProjectionEvidenceTier>` and skip them instead of panicking.
  3. Fix fixtures so candidate turns include at least one evidence kind.

Example fix

// before
fn evidence_tier(has_review: bool, has_branch: bool) -> ProjectionEvidenceTier {
    match (has_review, has_branch) {
        // ...
        (false, false) => unreachable!("candidate turns always have review or branch evidence"),
    }
}

// after
fn evidence_tier(has_review: bool, has_branch: bool) -> Option<ProjectionEvidenceTier> {
    match (has_review, has_branch) {
        (true, true) => Some(ProjectionEvidenceTier::Supporting),
        (true, false) => Some(ProjectionEvidenceTier::Direct),
        (false, true) => Some(ProjectionEvidenceTier::Possible),
        (false, false) => None, // not a candidate; skip
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// only feed turns that carry evidence into tier computation
let candidates: Vec<_> = turns
    .into_iter()
    .filter(|t| t.review_id.is_some() || t.branch_name.is_some())
    .collect();
for turn in candidates {
    let tier = evidence_tier(turn.review_id.is_some(), turn.branch_name.is_some());
    // ...
}

Type guard

fn evidence_tier_safe(has_review: bool, has_branch: bool) -> Option<ProjectionEvidenceTier> {
    match (has_review, has_branch) {
        (true, true) => Some(ProjectionEvidenceTier::Supporting),
        (true, false) => Some(ProjectionEvidenceTier::Direct),
        (false, true) => Some(ProjectionEvidenceTier::Possible),
        (false, false) => None, // not a candidate
    }
}

Prevention

When it happens

Trigger: A code path builds a candidate-turn list without the evidence filter and feeds it to `evidence_tier`; the upstream filter's condition is widened during a refactor; test fixtures synthesize turns with no review and no branch attached.

Common situations: Extending but-agentlog projections with new candidate sources; loosening the candidate query during debugging; fixture data that omits the evidence fields.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/a589c2b73bac9c78. Report an issue: GitHub.