Codex review pass on the escalation wedge. Reworks claim_session from read-then-write to a conditional UPDATE so two seniors racing can't both win, blocks the original engineer from claiming their own handoff, and filters self-escalated sessions out of the dashboard escalation queue. Also preassigns the handoff UUID before flush so the compatibility escalation_package payload carries it. Removes legacy frontend pickup state (claiming, handleStartHere) that broke tsc --noEmit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
HANDOFF.md
Last updated: 2026-04-30 (Codex review-fix pass)
Active task: Escalation Mode wedge — BROWSER QA COMPLETE + review fixes applied. Branch: feat/escalation-metric-endpoint. PR #155 ready to mark ready-for-review after committing this fix pass.
Where this session ended
Code-review fixes were applied after browser QA:
claim_sessionnow uses atomic conditionalUPDATE ... WHERE claimed_by IS NULLinstead of read-then-write, so simultaneous senior pickup cannot silently overwriteclaimed_by.- Original escalators cannot claim their own handoff. The escalation queue also excludes the current user's own escalated sessions, preventing the post-escalation dashboard from showing the junior their own handoff.
session.escalation_package["handoff_id"]is now populated from a preassigned UUID instead ofNonebefore flush.- Frontend build blockers removed: deleted unused legacy
claiming/handleStartHerepath inAssistantChatPageand unusedonStartHeredestructuring inHandoffContextScreen.
Validation:
git diff --check✅cd backend && pytest --override-ini='addopts=' tests/test_handoff_manager.py tests/test_session_handoffs_api.py tests/test_escalation_bus.py✅28 passed in 42.23scd frontend && /config/.bun/bin/bunx tsc -p tsconfig.app.json --noEmit --pretty false && /config/.bun/bin/bunx tsc -p tsconfig.node.json --noEmit --pretty false✅- Full frontend build could not complete because generated dirs are root-owned in this workspace:
frontend/node_modules/.tmp,frontend/node_modules/.vite-temp, and likelyfrontend/distproduce EACCES. Type errors from review are fixed.
Not testable in dev (known limitations):
- "Continue where X left off": requires senior to have existing task lane for session (won't occur on first pickup)
- Browser-level 409 race toast still requires two distinct senior accounts. Backend claim write is now atomic and covered by service/API tests for conflict, self-claim, and idempotent same-user retry.
Resume point — DO THIS NEXT
Ship: Commit this review-fix pass, then mark PR #155 ready-for-review and demo to stakeholder.
Optional before shipping:
- Record Loom demo walking through the escalation flow end-to-end
Key files changed this session
backend/app/services/handoff_manager.py—_generate_handoff_summaryreplaces old assessment pair;enrich_escalation_asyncunified;claim_sessioneager-loadshanded_off_by_userbackend/app/api/endpoints/ai_sessions.py— escalation queue excludes the current user's own escalationsbackend/app/api/endpoints/session_handoffs.py— self-claim returns 403backend/app/services/flowpilot_engine.py—generate_status_updateearly-returns saved prose forcontext='escalation'backend/app/schemas/session_handoff.py—handed_off_by_name: str | None = Noneaddedbackend/app/api/endpoints/session_handoffs.py— both create + claim endpoints passhanded_off_by_namefrontend/src/types/branching.ts—HandoffResponseupdated withsummary_prose,what_we_know,confidence: string,handed_off_by_namefrontend/src/components/flowpilot/HandoffContextScreen.tsx— 3-option CTA;hasTaskLane,activeOptionKey,onContinue/onAIAnalysis/onOwnThingpropsfrontend/src/components/assistant/TaskLane.tsx—id="task-lane-card-{idx}"on all card variantsfrontend/src/pages/AssistantChatPage.tsx—handleContinue,handleAIAnalysis,handleOwnThinghandlers; chip → card navigation;activeOptionKeystatebackend/tests/test_handoff_manager.py,backend/tests/test_session_handoffs_api.py— regression coverage for atomic/idempotent claim, self-claim rejection, queue self-exclusion, and pre-flush handoff ID
Watch-outs
- Dev stack: backend
:8000, frontend:5173, postgres:5433(docker-compose). HMR works. - Test users (Acme MSP, password
TestPass123!):engineer@resolutionflow.example.com(junior),teamadmin@resolutionflow.example.com(senior). handleAIAnalysispre-addsurlSessionIdtoloadedChatIdsRefbefore dismissing so the normal selectChat effect doesn't double-fire. It then callsselectChatmanually before sending the briefing.- Legacy
claiming/handleStartHereonAssistantChatPagewas removed;activeOptionKey !== nullis the active pre-claim processing signal. - The bus is acceptable for v1 pilot scale only (Railway single-replica). Redis pub/sub is the swap when horizontal scaling appears.