Repository navigation
fix(backend): guarded status transitions for error issues and investigations - #2310
Merged
Merged
Maple Review Bot / Maple / review
succeeded
Nov 6, 2026 in 2m 43s
Confidence 10/10 · No issues found
🟢 Confidence 10/10 · safe to merge. Every guarded write is pinned by a PGlite test at this head, including the loser-of-a-race 409 and the dropped late diagnosis.
no findings · tests covered · risk medium · 2/2 new units observable
Guards every error-issue and investigation status write on the state it was decided from, and pins the losers of a race to typed 409s or no-ops. The open TransitionConflictError from transition-row.ts is handled at all six call sites at this head, so it should stop reaching maple-api.
applyTransitionguards onfromStateand remaps the conflict to a 409- Tick regressions only reopen rows still
done; snooze/lease races are skipped - Manual investigation moves are limited to
MANUAL_INVESTIGATION_TRANSITIONS, reopen targets the run's own verdict - Late diagnosis/partial writes on settled runs are dropped with a span attribute
Production impact
Open errors in the changed files (1)
| Issue | Service | Occurrences | File |
|---|---|---|---|
| @maple/backend/TransitionConflictError | maple-api | 4 | packages/backend/src/platform/transition-row.ts |
After this merges, Maple checks whether they stop.
Production traffic of the changed files (last 7 days)
| File | Calls/day | Busiest operations |
|---|---|---|
packages/backend/src/services/errors/ErrorsService.ts |
71k | ErrorsService.expireLeases 71k/day, 0.0% err |
packages/backend/src/services/errors/apply-diagnosis.ts |
53 | ApplyDiagnosis.applyDiagnosisWrites 33/day, 0.0% errApplyDiagnosis.applyInconclusiveWrites 20/day, 0.0% err |
Telemetry this change adds and removes (2)
- ➕ attribute
maple.investigation.late_write_dropped·packages/backend/src/services/errors/apply-diagnosis.ts:132 - ➕ attribute
maple.error_tick.regressions_lost·packages/backend/src/services/errors/error-tick-persistence.ts:635
Ingest cost of new log lines
packages/backend/src/services/errors/ErrorsService.ts:1060: If this logs once per call, about 593 MB/month: the file's operations run 71k times a day.
What was checked
expireLeasesguardleaseExpiresAt.lt(nowMs)matches the preamble'sisNotNull+< nowfilter (ErrorsService.ts:1088, :1020)- The restart re-arm still works:
triageStatusFor("investigating")ispending, which skips the none/pending guard (InvestigationService.ts:78) - Each
transitionRowcaller (applyTransition, restart,updateStatus,apply-diagnosis,issue-hub,webhook-events) remaps or swallows the conflict
Observability coverage: 2 of 2 changes observable
| Change | Kind | Observable | Evidence |
|---|---|---|---|
| Errors tick per-row transitions (snooze wakeup, lease expiry) | background worker step | yes | skipLostTransition logs with orgId/issueId/via; ErrorsService.expireLeases span already exists |
| Investigation diagnosis / status writes | database write | yes | maple.investigation.late_write_dropped span attribute and Effect.logWarning in apply-diagnosis.ts:132 |
640481d · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.
Loading