feat(suggested-fix): add applied_pending status for deferred verification
Engineer applies a fix but can't verify yet (waiting on client power-cycle,
AD replication, async sync). Today the verifying banner forces a synchronous
verdict (worked / didn't / partial) — anything else means leaving the banner
stale or guessing wrong. This adds a fourth outcome that parks the fix in a
non-terminal "Awaiting verification" state with a reason ("waiting on what?")
and exposes it on the chat-anchored banner so the engineer doesn't lose track.
Backend
- New non-terminal status `applied_pending` parallel to `applied_partial`.
- New `pending_reason` column (nullable Text) — the "what are you waiting on?"
prose, mirrors `partial_notes`. Required when outcome=applied_pending.
- Outcome endpoint allows pending in/out transitions; pending stamps
applied_at but NOT verified_at (it's parked, not verified).
- Resolution-note + escalation-package prompts handle the new status:
resolution note frames the fix as provisional; escalation package surfaces
pending verification as the leading hypothesis with reference to what's
being waited on.
- Migration: add column + extend status CHECK constraint.
Frontend
- New `BannerMode = 'pending'` + `PendingBanner` component (info-tone,
parallel to PartialBanner) with worked / didn't / update-reason actions.
- VerifyingBanner overflow menu adds "Waiting to verify…".
- Nudge banner's "Still checking" button now actually records pending with
a reason, instead of just silencing for the session.
- AssistantChatPage banner-mode derivation maps applied_pending → 'pending'.
Tests: 4 new integration tests covering pending notes requirement, reason
storage + applied_at/verified_at semantics, pending→success transition,
and pending_reason update on re-PATCH.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,14 @@ export type FixStatus =
|
||||
| 'applied_success'
|
||||
| 'applied_failed'
|
||||
| 'applied_partial'
|
||||
| 'applied_pending'
|
||||
| 'dismissed'
|
||||
|
||||
export type FixOutcome =
|
||||
| 'applied_success'
|
||||
| 'applied_failed'
|
||||
| 'applied_partial'
|
||||
| 'applied_pending'
|
||||
| 'dismissed'
|
||||
|
||||
export interface AIOutcomeProposal {
|
||||
@@ -41,6 +43,7 @@ export interface SessionSuggestedFix {
|
||||
applied_at: string | null
|
||||
verified_at: string | null
|
||||
partial_notes: string | null
|
||||
pending_reason: string | null
|
||||
failure_reason: string | null
|
||||
ai_outcome_proposal: AIOutcomeProposal | null
|
||||
superseded_at: string | null
|
||||
@@ -126,11 +129,12 @@ export const sessionSuggestedFixesApi = {
|
||||
|
||||
/**
|
||||
* Record the outcome of applying a suggested fix. Transition rules:
|
||||
* - from `proposed` or `applied_partial`: any outcome is valid (partial is
|
||||
* parked, not terminal — engineer may update notes, abandon via dismiss,
|
||||
* or advance to success/failed).
|
||||
* - from `proposed`, `applied_partial`, or `applied_pending`: any outcome
|
||||
* is valid. Partial = "did some of it"; pending = "did all of it but
|
||||
* verification is deferred". Both are parked, not terminal.
|
||||
* - from a terminal status (`applied_success`, `applied_failed`, `dismissed`):
|
||||
* server returns 409.
|
||||
* - `applied_pending` requires `notes` (the "what are you waiting on?" reason).
|
||||
*/
|
||||
async patchOutcome(
|
||||
sessionId: string,
|
||||
|
||||
Reference in New Issue
Block a user