feat(l1): flywheel capture on resolve + engineer notification on escalate

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 20:49:28 -04:00
parent 68a4b99246
commit 80771b86b1
4 changed files with 116 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ from datetime import datetime, timezone
from typing import Optional
from uuid import UUID
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.audit import log_audit
@@ -15,6 +16,7 @@ from app.models.l1_walk_session import L1WalkSession
from app.models.user import User
from app.services import ai_tree_builder
from app.services import internal_ticket_service
from app.services.notification_service import notify
def _resolve_acting_as(user: User) -> Optional[str]:
@@ -263,6 +265,24 @@ async def resolve(
if proposal:
proposal.validated_by_outcome = True
# Flywheel capture: persist a validated FlowProposal for ai_build sessions
# resolved as helpful. Captures the AI-generated path as training signal.
if helpful and session.session_kind == "ai_build" and session.walked_path:
tree_structure = ai_tree_builder.normalize_walked_path(session.walked_path)
db.add(FlowProposal(
account_id=session.account_id,
l1_session_id=session.id,
source_session_id=None,
proposal_type="new_flow",
title=(session.resolution_notes or "AI L1 resolution")[:255],
proposed_flow_data={"tree_structure": tree_structure, "match_keywords": []},
source="ai_realtime_l1",
validated_by_outcome=True,
linked_ticket_id=session.ticket_id,
linked_ticket_kind=session.ticket_kind,
status="pending",
))
if session.ticket_kind == "internal":
await internal_ticket_service.update_status(
db,
@@ -339,6 +359,28 @@ async def escalate(
account_id=session.account_id,
acting_as=session.acting_as,
)
# Notify engineers (owner/admin/engineer roles) about the escalation.
eng_rows = await db.execute(
select(User.id).where(
User.account_id == session.account_id,
User.is_active.is_(True),
User.account_role.in_(("owner", "admin", "engineer")),
)
)
target_ids = [r[0] for r in eng_rows.all()]
await notify(
"l1.session.escalated",
session.account_id,
{
"problem_summary": session.ticket_id,
"session_id": str(session.id),
"reason_category": reason_category,
},
db,
target_user_ids=target_ids,
)
await db.flush()
return session