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) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 14:41:54 -04:00
parent cdd8bb05cc
commit 4a8e3ae954

View File

@@ -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):