feat: make issue reconciliation use configurable lanes#432
Conversation
There was a problem hiding this comment.
AI Automated Review
Full PR review.
Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic) — escalated (fast_low_confidence)
PR Review: feat: make issue reconciliation use configurable lanes
Summary
This PR implements config-aware lane classification for issue reconciliation, replacing hardcoded "normal" | "escalated" | "backlog" lane IDs with dynamically resolved lane IDs from the LaneConfig system. The implementation adds a new LaneSignals interface, a classifyLaneFromSignals() function, and supporting helpers (getDefaultClaimableLane(), getEscalationLane()), and updates both issue-lane.ts and issue-reconciliation.ts to use the config-aware classification path.
Change-by-Change Findings
src/lib/lane-config.ts
- Adds
role?: "default" | "escalation"field toLaneConfiginterface, enabling explicit role assignment beyond the claimable/non-claimable distinction. - Adds
role: "default"to the defaultnormallane androle: "escalation"to the defaultescalatedlane inDEFAULT_LANE_CONFIG. - Introduces
getDefaultClaimableLane(): prefers explicitrole="default", falls back to first claimable lane. - Introduces
getEscalationLane(): prefers explicitrole="escalation", falls back to default claimable lane (ensuring a valid lane id is always returned). - Introduces
LaneSignalsinterface:{ isBacklog: boolean, isEscalation: boolean }. - Introduces
classifyLaneFromSignals(signals): maps backlog/escalation signals to configured lanes with safe fallbacks. This is the key guarantee — the function never returns an unknown lane id; it always falls back to the default claimable lane or"normal".
src/lib/issue-lane.ts
buildLaneClassificationPrompt(): now callsgetConfiguredLanes()andgetDefaultClaimableLane()/getEscalationLane()/getBacklogLane()to dynamically build lane definitions and routing rules in the prompt, rather than hardcodingnormal/escalated/backlog.classifyByHeuristics(): now callsclassifyLaneFromSignals()instead of returning hardcoded"normal","escalated", or"backlog"strings. All three return paths are updated.
src/lib/issue-reconciliation.ts
- Hoists shared signal lists (
ESCALATION_KEYWORDS,BACKLOG_SIGNALS,ESCALATION_LABELS) to module scope for use by bothevaluateLaneSignals()andshouldReclassifyStaleBacklog(). - Introduces
evaluateLaneSignals(): returns{ isBacklog, isEscalation, reason }— this is the structured signal layer that decouples the heuristics from lane resolution. - Refactors
classifyLaneByHeuristics(): delegates toevaluateLaneSignals()and thenclassifyLaneFromSignals(). Return type changes from"normal" | "escalated" | "backlog"tostring(a configured lane id). - Refactors
shouldReclassifyStaleBacklog(): return type changes from"normal" | "escalated" | nulltostring | null. The fallback path now callsgetDefaultClaimableLane()?.id ?? "normal"instead of hardcoding"normal".
Standards Compliance
- ✅ No agent-specific names in generic docs: No agent names appear in the new classification logic.
- ✅ Prisma schema: No schema changes in this PR.
- ✅ API routes: N/A — all changes are library-level.
- ✅ Error handling:
classifyLaneFromSignals()includes safe fallbacks when no matching lane is configured;getEscalationLane()falls back to the default claimable lane so it never returns undefined in the hot path. - ✅ Validation: Lane ids are validated by the existing
isValidLane()which delegates tolane-config. The newclassifyLaneFromSignals()only returns ids from the configured set.
Linked Issue Fit
Issue PR 385 acceptance criteria are fully addressed:
| Criterion | Status |
|---|---|
| Default lane classification backward-compatible | ✅ Verified: default config tests assert normal/escalated/backlog output |
| Reconciliation rejects/avoids unknown lane IDs | ✅ Verified: classifyLaneFromSignals() only returns configured lane ids |
| Single claimable lane config can classify actionable issues | ✅ Verified: tests with work/backlog lanes |
| Multi-lane config can classify to more than two claimable lanes | ✅ Verified: tests with fast/slow/parked and alpha/beta/gamma |
| Backlog/not-ready issues go to non-claimable lane | ✅ Verified: tests assert parked/gamma etc. |
| Tests cover default, single, and custom multi-lane configs | ✅ Verified: 118 + 242 + 151 new test cases across 3 test files |
Evidence Provider Findings
No evidence providers were configured for this review.
Tool Harness Findings
Tool harness results are not applicable to this PR (no evidence provider findings to evaluate).
CI Check Results
- Validate: completed successfully
- Docker Build: completed successfully
Both CI checks passed. No blockers.
Unknowns / Needs Verification
No unknowns. The diff is self-contained and thoroughly tested. The classification functions use safe fallbacks at every boundary. The existing isBacklogLane() helper (unchanged in this PR) continues to validate backlog status against the configured lane set.
Recommendation
Approve. This PR cleanly refactors lane classification to be config-aware while preserving full backward compatibility. The test coverage is comprehensive, the safe-fallback chain in classifyLaneFromSignals() guarantees no unknown lane ids are ever returned, and all acceptance criteria from issue PR 385 are demonstrably satisfied.
Closes #385