From 4a8e3ae954450984b1f542b3e2e3f98f5b08a0d7 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Thu, 23 Apr 2026 14:41:54 -0400 Subject: [PATCH] feat(pilot): pydantic schemas for fix outcome patch Adds FixStatus literal (5 values matching the DB check constraint), extends SessionSuggestedFixResponse with outcome fields, and introduces SessionSuggestedFixOutcomeRequest for the PATCH /outcome endpoint coming in Task 3. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/app/schemas/session_suggested_fix.py | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/backend/app/schemas/session_suggested_fix.py b/backend/app/schemas/session_suggested_fix.py index df55fc1f..816874b0 100644 --- a/backend/app/schemas/session_suggested_fix.py +++ b/backend/app/schemas/session_suggested_fix.py @@ -12,6 +12,17 @@ from pydantic import BaseModel, Field UserDecision = Literal["one_off", "draft_template", "build_template", "dismissed"] +# "dismissed" here is the outcome dimension — orthogonal to UserDecision's +# "dismissed" (script-path choice), though the migration backfill aligns +# them for pre-existing rows. +FixStatus = Literal[ + "proposed", + "applied_success", + "applied_failed", + "applied_partial", + "dismissed", +] + class SessionSuggestedFixResponse(BaseModel): id: UUID @@ -25,6 +36,12 @@ class SessionSuggestedFixResponse(BaseModel): user_decision: UserDecision | None superseded_at: datetime | None created_at: datetime + status: FixStatus + applied_at: datetime | None + verified_at: datetime | None + partial_notes: str | None + failure_reason: str | None + ai_outcome_proposal: dict[str, Any] | None model_config = {"from_attributes": True} @@ -71,6 +88,30 @@ class SessionSuggestedFixDecisionResponse(BaseModel): ) +# Subset of FixStatus that the engineer can set via the outcome endpoint — +# `proposed` is excluded because you can't un-decide a fix back to "proposed". +FixOutcome = Literal[ + "applied_success", "applied_failed", "applied_partial", "dismissed" +] + + +class SessionSuggestedFixOutcomeRequest(BaseModel): + """Engineer-reported outcome of applying a suggested fix. + + Writes to session_suggested_fixes.status and companion columns. This is + orthogonal to `user_decision` (which records which script-path the + engineer took); outcome captures whether the fix actually worked. + + Allowed transitions: + - from `proposed`: any of applied_success | applied_failed | applied_partial | dismissed + - from `applied_partial`: applied_success | applied_failed (partial is not terminal) + - from any terminal outcome: no change (server returns 409) + """ + outcome: FixOutcome + # Required for applied_partial, optional for applied_failed, ignored otherwise. + notes: str | None = Field(None, max_length=500) + + # ── Resolution note preview ──────────────────────────────────────────────── class ResolutionNotePreviewResponse(BaseModel):