feat(pilot): Phase 4 — Resolve + Escalate PSA writebacks with status verification
All checks were successful
Mirror to GitHub / mirror (push) Successful in 11s

Wires the preview popover's Confirm & post action to ConnectWise (and,
via the provider pattern, any future PSA). Adds the parallel Escalate
flow with the handoff-oriented five-section markdown. Sessions without a
linked PSA ticket resolve/escalate locally — markdown stored, status
flipped, nothing posted externally.

Backend:
- EscalationPackageGeneratorService: Sonnet, five sections (Problem /
  What we've confirmed / What we've tried / Current hypothesis /
  Suggested next steps). Shares the preview_cache with a separate KIND
  so Resolve and Escalate previews for the same state coexist.
- PSAWritebackService: post_resolution_note (RESOLUTION note type,
  customer-visible), post_escalation_package (INTERNAL_ANALYSIS,
  handoff for the next engineer only), transition_ticket_status with
  mandatory re-fetch verification. PSAStatusVerificationError surfaces
  loudly when CW silently rejects a status change — the
  ConnectWise anti-pattern CLAUDE.md flags.
- Endpoints:
  * POST /ai-sessions/{id}/escalation-package/preview
  * POST /ai-sessions/{id}/resolution-note/post
  * POST /ai-sessions/{id}/escalation-package/post
  Outcomes: "resolved" / "escalated" with external_id + verified status,
  "resolved_local" / "escalated_local" when no PSA linked.
- Target CW status IDs live in account_settings.preferences
  (cw_resolved_status_id, cw_escalated_status_id). When unset, the post
  proceeds without a status transition — response includes a
  status_transition_skipped_reason rather than silently erroring.
- 7 tests: local-only path, PSA happy path with verified transition,
  status verification failure → 502, skipped transition when
  unconfigured, 409 on already-resolved re-post, escalate parallel path,
  internal-analysis note type enforced.

Frontend:
- ResolutionNotePreview now kind-parameterized ('resolve' | 'escalate')
  with inline edit + Confirm & post. Preview loads from the matching
  backend endpoint; posting calls the matching endpoint; outcome toast
  surfaces the verified CW status or the local-only result.
- AssistantChatPage: previewKind state replaces previewOpen; two toggle
  buttons (Preview Resolve note / Escalate instead) in the lane's bottom
  slot. handleConfirmPost dispatches by kind.

Verified 2026-04-22:
- Local-only Resolve + Escalate round-trip against the dev stack.
- Live Sonnet escalation-package preview; cache hit on repeat call
  with no state change (separate cache kind from resolution-note).
- PSA post + status-verification paths covered by mocked-provider pytest
  cases. Live CW round-trip pending a test CW instance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 23:54:54 -04:00
parent 7ccf4c602b
commit 8fd2c1bac6
10 changed files with 1337 additions and 46 deletions

View File

@@ -2,8 +2,8 @@
> **Target:** Transform `/assistant` (ResolutionAssist) into the new unified `/pilot` (FlowPilot) surface.
> **Audience:** Claude Code (implementation) and Codex (review) reviewed by Michael (owner).
> **Status:** Phases 03 implemented and verified end-to-end against the dev stack. Phase 4 next.
> **Last updated:** April 22, 2026 (Phase 3Suggested fix + Resolve preview — committed; live Sonnet preview + state_version cache verified)
> **Status:** Phases 04 implemented and verified end-to-end against the dev stack. Phase 5 next.
> **Last updated:** April 22, 2026 (Phase 4Resolve + Escalate writebacks — committed; local-only and mocked-PSA paths verified, live CW ticket round-trip pending a test instance)
---
@@ -770,6 +770,20 @@ git commit -m "feat(pilot): add suggested fix tracking and Resolve note preview
- Simulate CW silently rejecting a status change — verify the app surfaces an error, not silent success.
- Attempt to Resolve without a linked PSA ticket — session marks resolved locally without erroring; markdown stored in `resolution_note_markdown`.
**Verified on 2026-04-22:**
- Local-only Resolve + Escalate confirmed end-to-end against the dev stack
(no PSA instance wired): markdown stored, session.status flips, 409 on
re-post.
- Escalation-package preview generates well-formed five-section markdown
from a single fact (real Sonnet); second preview call with no state
change returns `from_cache=true`, confirming the cache-kind separation
from resolution-note previews.
- PSA post + status-verification paths covered by mocked-provider pytest
cases: happy path, silent-rejection → 502 with clear detail, skipped
transition when `cw_resolved_status_id` unset, internal-analysis note
type used for escalation handoffs. Live CW round-trip still TODO once a
test instance is wired.
```
git commit -m "feat(pilot): wire Resolve and Escalate to ConnectWise writeback with status verification"
```