feat(pilot): PATCH /suggested-fixes/:id/outcome endpoint + tests

Records engineer-reported outcome (applied_success|applied_failed|
applied_partial|dismissed). Enforces transition rules (partial → success/
failed allowed; terminal outcomes return 409) and notes requirements
(applied_partial requires notes).

Sets verified_at on success/failure, stamps applied_at if not already
set (handles the case where the AI [FIX_OUTCOME] marker fires before
the engineer clicks Apply).

Also fixes pre-existing test-infrastructure bug: network_diagram.py used
bare string server_default="'[]'" for JSONB columns, which asyncpg
rejects during test schema creation. Changed to text("'[]'::jsonb") to
match the pattern used by script_template.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 14:50:21 -04:00
parent 4a8e3ae954
commit 8988dbc885
5 changed files with 280 additions and 12 deletions

View File

@@ -238,9 +238,11 @@ class SessionSuggestedFixOutcomeRequest(BaseModel):
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)
- from `proposed` or `applied_partial`: any outcome is valid
(partial is parked, not terminal — the engineer may update notes,
abandon via dismiss, or advance to success/failed)
- from any terminal outcome (`applied_success`, `applied_failed`,
`dismissed`): server returns 409
"""
outcome: Literal[
"applied_success", "applied_failed", "applied_partial", "dismissed"
@@ -769,9 +771,9 @@ Add a new method on `sessionSuggestedFixesApi` (after `recordDecision`):
```ts
/**
* Record the outcome of applying a suggested fix. Transitions:
* - from 'proposed': any non-proposed outcome
* - from 'applied_partial': applied_success | applied_failed
* - terminal statuses are locked (server returns 409)
* - from 'proposed' or 'applied_partial': any outcome is valid
* (partial→partial updates notes, partial→dismissed abandons)
* - terminal statuses (applied_success, applied_failed, dismissed) are locked (server returns 409)
*/
async patchOutcome(
sessionId: string,