fix(engine): defer validator fail to inconclusive while the linked task is unmerged#1917
fix(engine): defer validator fail to inconclusive while the linked task is unmerged#1917flexi767 wants to merge 1 commit into
Conversation
…sk is unmerged A "fail" verdict from runFeatureValidation is only trustworthy once the linked task's code has actually landed (column done/archived). When the task is still mid-pipeline — an in-review PR, an external merge train, a deferred base sync — the validator judged a checkout that predates the merge and reports the work as missing, minting a duplicate Fix feature (and board task) for code that is about to land. Route that case to handleValidationInconclusive (R21: no Fix feature, run completed as blocked, distinguishable verification_inconclusive event) so a later validation judges the merged code instead. The guard fails open: a missing/unlinked task, an unreadable task store, or an unknown column all keep the existing handleValidationFail path — it can only ever defer a fail on affirmative evidence of an unmerged column, never suppress one on missing data. The vanilla done-triggered flow (scheduler fires processTaskOutcome on toColumn === "done") is unaffected; the guard matters for recovery-path validations and for deployments whose tasks merge through external pipelines. Incident context: a mission validator racing an external merge train failed four features while their tasks were in-review, and the four generated Fix tasks' file-scope leases on hot shared files serialized the entire board. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis change modifies ChangesPremerge Guard for Validation Fail Verdict
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| const column = linkedTask?.column; | ||
| if (!column || column === "done" || column === "archived") return null; | ||
| return column; |
There was a problem hiding this comment.
When a project uses a custom column that means completed, such as an external merge or release column, this helper treats it as pre-merge because it returns every truthy value except done and archived. A real validator failure for that linked task is then completed as inconclusive, no Fix feature is created, and the feature can stay blocked instead of getting repaired.
| const column = linkedTask?.column; | |
| if (!column || column === "done" || column === "archived") return null; | |
| return column; | |
| const column = linkedTask?.column; | |
| if (column === "triage" || column === "todo" || column === "in-progress" || column === "in-review") return column; | |
| return null; |
Problem
MissionExecutionLoop.runFeatureValidationtreats a validator "fail" verdict as authoritative regardless of whether the linked task's code has actually landed. When validation fires while the task is still mid-pipeline — an in-review PR, an external merge train, a deferred base sync — the validator judges a checkout that predates the merge, concludes the feature "is not present", andhandleValidationFailmints a Fix feature for work that is already done.We hit this in production (2026-07-05): a recovery-path validation ran against four features whose implementing tasks were in-review in an external merge pipeline. All four "failed" → four duplicate Fix tasks were created one minute after the real work merged. Worse, the Fix tasks' planned file scopes included hot shared files, so their file-scope leases serialized the entire board until they were manually archived.
Fix
Before dispatching a
failverdict, resolve the linked task's column. If it affirmatively shows the task has not completed (any column other thandone/archived), route the outcome tohandleValidationInconclusive(R21 — completes the run asblocked, logsverification_inconclusive, notifies autopilot, spawns no Fix feature) with a "code not merged yet — validation deferred" reason. A later validation (post-merge recovery pass) judges the real merged code.Fails open by design — the guard may only ever defer a fail, never suppress one on missing data. Missing
taskId, missing task, unreadable store, or unknown column all fall through to the normalhandleValidationFailpath:The vanilla flow is unaffected: the scheduler triggers validation on
toColumn === "done", so by the time a normally-triggered validation runs the task is alreadydoneand the guard is a no-op. Only recovery-path / re-validation runs that race an unmerged task are deferred.Tests
Three new tests in
mission-execution-loop.test.ts(premerge guarddescribe):in-review→ routes to inconclusive: no Fix feature, run completed asblocked,validation:inconclusiveemitted (notvalidation:failed),verification_inconclusivemission event loggeddone→ normal fail path: Fix feature created,validation:failedemittedtaskStore.getTaskrejects → fails open to the normal fail pathnpx vitest run src/__tests__/mission-execution-loop.test.ts: 58/58 green.npx tsc --noEmit: clean. Full engine suite: the 46 failures across 24 files present on my branch fail identically on clean17c4007(verified by re-running the same files on a detached checkout of upstream main) — all pre-existing/environment-dependent, none related to this change.Summary by CodeRabbit