diff --git a/.ai/DECISIONS.md b/.ai/DECISIONS.md index 2f8b7153..0e06d4dc 100644 --- a/.ai/DECISIONS.md +++ b/.ai/DECISIONS.md @@ -13,6 +13,25 @@ --- +## 2026-04-30 — Allow `escalated_to_id` to send chat messages in claimed sessions + +**Context:** During browser QA, clicking "Get AI analysis" on the magic-moment screen returned `POST /ai-sessions/{id}/chat → 400`. The senior tech who claimed the session is stored as `escalated_to_id` on `AISession`, not `user_id` (which remains the junior who created the session). `unified_chat_service.send_chat_message` queried `WHERE ai_sessions.user_id = :user_id`, so the senior's ID never matched and the endpoint rejected the request. + +**Decision:** Extend the ownership check in `send_chat_message` to `OR ai_sessions.escalated_to_id = :user_id` using SQLAlchemy `or_()`. This is the minimal, correct fix: the session model already has a semantically valid "also owns" field for the claiming senior; extending the WHERE clause makes that ownership real. + +**Rejected:** + +- **Transfer `user_id` to the senior on claim.** Breaks the audit trail — `user_id` is the originating engineer throughout the session lifecycle. Any query scoped to "sessions this engineer worked on" would silently lose the junior's history. +- **A separate `can_send_message` service method.** Adds indirection with no benefit for v1. One `or_()` line in the existing query is sufficient. +- **Checking a role/permission flag instead.** Role gating (engineer/admin) already happens at the claim endpoint. The chat-send check is about session ownership, not role. Mixing the two concerns would be confusing. + +**Consequences:** +- Seniors can send AI briefings and continue chat work in sessions they have claimed. Core escalation pickup flow unblocked. +- Any future caller of `send_chat_message` should be aware that "user_id or escalated_to_id" is the ownership rule. The service-level check is the single enforcement point. +- `user_id` remains the originating engineer for all audit, history, and analytics queries. No data migration needed. + +--- + ## 2026-04-29 — Consolidate the three per-escalation AI calls into one structured generation **Context:** A single user-initiated escalation currently triggers three separate Sonnet calls, all summarizing the same source material (session state, steps taken, "what we know") from slightly different angles: diff --git a/.ai/SESSION_LOG.md b/.ai/SESSION_LOG.md index 3698a010..8945a0cd 100644 --- a/.ai/SESSION_LOG.md +++ b/.ai/SESSION_LOG.md @@ -12,6 +12,26 @@ --- +## 2026-04-30 — Claude Code — Browser QA pass complete; chat ownership bug found and fixed; PR #155 ready + +- Ran full browser QA pass on the escalation mode feature using gstack `/qa` skill. +- **Critical bug found and fixed (commit `dc69c9d`):** `POST /ai-sessions/{id}/chat → 400` when senior clicked "Get AI analysis" on the magic-moment screen. Root cause: `unified_chat_service.send_chat_message` checked `AISession.user_id == user_id` only; senior is stored as `escalated_to_id`, not `user_id`. Fix: `or_(AISession.user_id == user_id, AISession.escalated_to_id == user_id)` in the WHERE clause. +- **All 7 QA scenarios passed:** + - Post-escalation redirect: junior routed to `/` with "Session escalated" toast. + - Magic-moment screen: header, metadata, two-column AI assessment, 2-option CTA rendered correctly. + - "I'll take it from here": claim → dismiss overlay → composer focused. + - "Get AI analysis": claim → briefing sent → AI responded → task lane populated (after `dc69c9d` fix). + - Task lane copy button: toast + checkmark visual feedback. + - Chip expansion: inline detail card + "Open in Tasks panel" scroll. + - Post-claim toolbar re-open: dismissible mode with Close-only CTA. +- **Known non-blockers:** "Continue where X left off" path untestable on first pickup (`hasTaskLane=false` is correct v1 behavior). 409 race condition untestable with one senior account; backend logic code-reviewed and correct. +- Backend tests: 17/17 pass. +- Updated `HANDOFF.md` to reflect QA complete; updated `CURRENT_TASK.md` status to engineering+QA complete; appended architectural decision to `DECISIONS.md`. +- Branch `feat/escalation-metric-endpoint` is ready for PR #155 to be marked ready-for-review. +- **Files touched this session:** `backend/app/services/unified_chat_service.py`, `.ai/HANDOFF.md`, `.ai/CURRENT_TASK.md`, `.ai/DECISIONS.md`, `.ai/SESSION_LOG.md`. + +--- + ## 2026-04-29 04:30 EDT — Claude Code — Live QA bash, pickup bug fixes, AI summary consolidation surfaced - User on a freshly swapped computer ran the live QA flow. Identified two bugs missed by static analysis from the previous session: