docs(pilot): fix Phase 8 column + commit-SHA references

Correct the FLOWPILOT-MIGRATION.md stale references to a non-existent
ai_sessions.fix_outcome column — the actual implementation added six
columns to session_suggested_fixes. Also fix a stale first-commit SHA
(6721b84 → cdd8bb0, the former was amended away).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 17:42:51 -04:00
parent 2a54127a54
commit a47ce07326
4 changed files with 2337 additions and 4 deletions

View File

@@ -0,0 +1,805 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>FlowPilot — Post-apply outcome states</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Bricolage+Grotesque:wght@500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-sidebar: #0e1016;
--bg-page: #16181f;
--bg-card: #1e2028;
--bg-elevated: #2a2d38;
--border-default: rgba(148, 163, 184, 0.12);
--border-hover: rgba(148, 163, 184, 0.22);
--text-heading: #f1f5f9;
--text-primary: #e2e8f0;
--text-muted-foreground: #94a3b8;
--text-muted: #64748b;
--accent: #60a5fa;
--accent-dim: rgba(96, 165, 250, 0.10);
--accent-dim-strong: rgba(96, 165, 250, 0.16);
--accent-border: rgba(96, 165, 250, 0.30);
--warning: #fbbf24;
--warning-dim: rgba(251, 191, 36, 0.10);
--warning-dim-strong: rgba(251, 191, 36, 0.16);
--warning-border: rgba(251, 191, 36, 0.32);
--success: #34d399;
--success-dim: rgba(52, 211, 153, 0.10);
--success-dim-strong: rgba(52, 211, 153, 0.16);
--success-border: rgba(52, 211, 153, 0.30);
--info: #67e8f9;
--info-dim: rgba(103, 232, 249, 0.10);
--info-dim-strong: rgba(103, 232, 249, 0.16);
--info-border: rgba(103, 232, 249, 0.30);
--danger: #f87171;
--danger-dim: rgba(248, 113, 113, 0.10);
--danger-dim-strong: rgba(248, 113, 113, 0.16);
--danger-border: rgba(248, 113, 113, 0.30);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
background: var(--bg-sidebar);
color: var(--text-primary);
font-family: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
.page {
max-width: 1680px;
margin: 0 auto;
padding: 32px 24px 72px;
}
.page-header { margin-bottom: 24px; }
.page-title {
font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600;
font-size: 22px;
color: var(--text-heading);
letter-spacing: -0.01em;
}
.page-sub {
margin-top: 6px;
color: var(--text-muted-foreground);
font-size: 13px;
max-width: 1020px;
line-height: 1.55;
}
/* ====== Shared button styles ====== */
.btn {
appearance: none;
border: 1px solid var(--border-default);
background: var(--bg-card);
color: var(--text-primary);
padding: 8px 12px;
border-radius: 8px;
font-family: inherit;
font-weight: 500;
font-size: 12.5px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
cursor: pointer;
transition: border-color 0.12s, background-color 0.12s, color 0.12s;
white-space: nowrap;
}
.btn:hover { border-color: var(--border-hover); background: var(--bg-elevated); }
.btn-ghost {
background: transparent;
border-color: transparent;
color: var(--text-muted-foreground);
padding: 8px 10px;
}
.btn-ghost:hover {
background: rgba(148, 163, 184, 0.08);
color: var(--text-primary);
border-color: transparent;
}
.icon-btn {
width: 30px; height: 30px; padding: 0;
background: transparent; border: 1px solid transparent;
color: var(--text-muted-foreground);
}
.icon-btn:hover { background: rgba(148, 163, 184, 0.08); color: var(--text-primary); }
.btn-success {
background: var(--success); color: #0a1a12; border-color: transparent; font-weight: 600;
}
.btn-success:hover { background: #55e0af; color: #0a1a12; }
.btn-danger-outline {
background: transparent; color: var(--danger); border-color: var(--danger-border);
}
.btn-danger-outline:hover { background: var(--danger-dim); color: var(--danger); border-color: var(--danger); }
.btn-danger {
background: var(--danger); color: #180808; border-color: transparent; font-weight: 600;
}
.btn-danger:hover { background: #fa8a8a; color: #180808; }
/* ====== Frame ====== */
.frame {
background: var(--bg-page);
border: 1px solid var(--border-default);
border-radius: 14px;
overflow: hidden;
display: grid;
grid-template-columns: 1fr 380px;
height: 760px;
}
.chat {
display: flex; flex-direction: column;
background: var(--bg-page);
min-width: 0;
}
.chat-head {
padding: 14px 20px;
border-bottom: 1px solid var(--border-default);
background: var(--bg-sidebar);
display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.chat-head-title {
font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600; font-size: 14px; color: var(--text-heading);
}
.chat-head-sub {
font-size: 11.5px; color: var(--text-muted);
font-family: 'JetBrains Mono', monospace;
}
.chat-scroll {
flex: 1; overflow-y: auto;
padding: 24px 28px 16px;
display: flex; flex-direction: column; gap: 16px;
}
.msg { max-width: 640px; display: flex; gap: 10px; align-items: flex-start; }
.msg.user { align-self: flex-end; }
.msg-av {
width: 26px; height: 26px; border-radius: 50%;
flex-shrink: 0; font-size: 11px; font-weight: 600;
display: flex; align-items: center; justify-content: center; margin-top: 2px;
}
.msg.user .msg-av { background: var(--accent-dim); color: var(--accent); border: 1px solid var(--accent-border); }
.msg.ai .msg-av { background: var(--warning-dim); color: var(--warning); border: 1px solid var(--warning-border); }
.msg.system .msg-av { background: rgba(148,163,184,0.08); color: var(--text-muted); border: 1px solid var(--border-default); }
.msg-body {
background: var(--bg-card); border: 1px solid var(--border-default);
border-radius: 10px; padding: 10px 13px; font-size: 13px; color: var(--text-primary);
line-height: 1.55;
}
.msg.user .msg-body { background: var(--accent-dim); border-color: var(--accent-border); color: var(--text-heading); }
.msg.system .msg-body { background: transparent; border-style: dashed; color: var(--text-muted); font-size: 12px; font-style: italic; }
.msg-meta {
margin-top: 4px; font-size: 10.5px; color: var(--text-muted);
font-family: 'JetBrains Mono', monospace;
}
.composer-wrap { border-top: 1px solid var(--border-default); background: var(--bg-page); position: relative; }
.composer { padding: 14px 20px 16px; display: flex; align-items: flex-end; gap: 10px; }
.composer-input {
flex: 1; min-height: 44px; background: var(--bg-card);
border: 1px solid var(--border-default); border-radius: 10px;
padding: 10px 14px; color: var(--text-muted-foreground);
font-size: 13px; line-height: 1.4;
display: flex; align-items: center;
}
.composer-send {
width: 44px; height: 44px; border-radius: 10px;
background: var(--accent); color: #0a0d14; border: 0;
display: flex; align-items: center; justify-content: center; cursor: pointer;
}
/* ====== Banner generic ====== */
.banner {
position: relative;
padding: 12px 20px 14px;
display: flex; gap: 14px; align-items: flex-start;
border-top-width: 1px; border-top-style: solid;
animation: fadeIn 260ms ease-out both;
}
.banner::before {
content: '';
position: absolute; left: 0; top: 0; bottom: 0;
width: 3px;
}
@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
.banner-icon {
width: 28px; height: 28px; border-radius: 7px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; margin-top: 2px;
}
.banner-body { flex: 1; min-width: 0; }
.banner-head {
display: flex; align-items: center; gap: 8px;
font-size: 10px; font-weight: 600; letter-spacing: 1.2px;
text-transform: uppercase; font-family: 'Bricolage Grotesque', sans-serif;
}
.banner-title {
margin-top: 3px; font-size: 14px; font-weight: 600;
color: var(--text-heading); line-height: 1.35; letter-spacing: -0.005em;
}
.banner-note {
margin-top: 3px; font-size: 12.5px; color: var(--text-muted-foreground);
line-height: 1.5;
}
.banner-actions {
display: flex; gap: 8px; align-items: center;
flex-shrink: 0; padding-top: 2px;
}
.pill {
padding: 2px 7px; border-radius: 999px;
font-size: 10.5px; font-weight: 700; letter-spacing: 0.5px;
font-variant-numeric: tabular-nums;
}
/* Verifying — amber pulse, mirrors the proposed color but with pulse */
.banner-verify {
background: linear-gradient(180deg, var(--warning-dim-strong) 0%, var(--warning-dim) 100%);
border-top-color: var(--warning-border);
}
.banner-verify::before { background: var(--warning); }
.banner-verify .banner-icon {
background: var(--warning-dim-strong); border: 1px solid var(--warning-border); color: var(--warning);
position: relative;
}
.banner-verify .banner-icon::after {
content: ''; position: absolute; inset: -3px; border-radius: 9px;
box-shadow: 0 0 0 0 rgba(251, 191, 36, 0.45);
animation: pulseAmber 1.6s infinite;
}
@keyframes pulseAmber {
0% { box-shadow: 0 0 0 0 rgba(251, 191, 36, 0.45); }
70% { box-shadow: 0 0 0 10px rgba(251, 191, 36, 0); }
100% { box-shadow: 0 0 0 0 rgba(251, 191, 36, 0); }
}
.banner-verify .banner-head { color: var(--warning); }
.banner-verify .pill { background: rgba(251, 191, 36, 0.20); color: var(--warning); }
/* Partial — muted info/cyan to communicate "parked, outcome unknown" */
.banner-partial {
background: linear-gradient(180deg, var(--info-dim-strong) 0%, var(--info-dim) 100%);
border-top-color: var(--info-border);
}
.banner-partial::before { background: var(--info); }
.banner-partial .banner-icon { background: var(--info-dim-strong); border: 1px solid var(--info-border); color: var(--info); }
.banner-partial .banner-head { color: var(--info); }
.banner-partial .pill { background: rgba(103, 232, 249, 0.18); color: var(--info); }
/* AI-inferred — accent blue, AI-sourced */
.banner-ai {
background: linear-gradient(180deg, var(--accent-dim-strong) 0%, var(--accent-dim) 100%);
border-top-color: var(--accent-border);
}
.banner-ai::before { background: var(--accent); }
.banner-ai .banner-icon { background: var(--accent-dim-strong); border: 1px solid var(--accent-border); color: var(--accent); }
.banner-ai .banner-head { color: var(--accent); }
.banner-ai .pill { background: rgba(96, 165, 250, 0.20); color: var(--accent); }
/* Nudge — compact strip */
.banner-nudge {
padding: 8px 20px;
background: var(--warning-dim);
border-top-color: var(--warning-border);
align-items: center;
gap: 10px;
}
.banner-nudge::before { background: var(--warning); }
.banner-nudge .nudge-icon {
width: 16px; height: 16px; flex-shrink: 0; color: var(--warning);
}
.banner-nudge .nudge-title {
flex: 1; font-size: 12.5px; color: var(--text-primary); font-weight: 500;
}
/* ====== Task lane ====== */
.lane {
border-left: 1px solid var(--border-default);
background: var(--bg-sidebar);
display: flex; flex-direction: column; min-height: 0;
}
.lane-head {
padding: 14px 16px;
border-bottom: 1px solid var(--border-default);
display: flex; align-items: center; justify-content: space-between;
}
.lane-head-label {
font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600; font-size: 13px; color: var(--text-heading);
}
.lane-body {
flex: 1; overflow-y: auto;
padding: 14px 14px 10px;
display: flex; flex-direction: column; gap: 16px;
}
.section-label {
display: flex; align-items: center; gap: 8px;
font-size: 10px; font-weight: 600; letter-spacing: 1.2px;
text-transform: uppercase; color: var(--text-muted-foreground);
padding: 0 2px 8px;
}
.dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.dot-accent { background: var(--accent); }
.dot-danger { background: var(--danger); }
.section-meta {
color: var(--text-muted); font-weight: 500; letter-spacing: 0; text-transform: none;
}
.fact {
background: var(--bg-card); border: 1px solid var(--border-default);
border-left: 3px solid var(--accent); border-radius: 8px;
padding: 10px 12px;
}
.fact + .fact { margin-top: 8px; }
.fact-title { font-size: 12.5px; font-weight: 500; color: var(--text-heading); line-height: 1.4; }
.fact-meta { margin-top: 3px; font-size: 10.5px; color: var(--text-muted); font-family: 'JetBrains Mono', monospace; }
.failed-pill {
padding: 9px 11px; background: var(--bg-card);
border: 1px dashed var(--danger-border); border-radius: 8px;
display: flex; align-items: center; gap: 8px;
font-size: 11.5px; color: var(--text-muted-foreground);
}
.failed-pill-title { flex: 1; color: var(--text-heading); font-weight: 500; }
.failed-pill-badge {
padding: 1px 6px; border-radius: 4px; font-size: 9.5px;
font-weight: 700; letter-spacing: 0.4px;
background: var(--danger-dim); color: var(--danger);
text-transform: uppercase;
}
.action-bar {
border-top: 1px solid var(--border-default);
padding: 12px 14px 14px;
display: flex; gap: 8px;
position: relative;
}
.btn-escalate { flex: 0 0 auto; min-width: 96px; background: transparent; color: var(--text-muted-foreground); }
.btn-resolve {
flex: 1; background: var(--accent); color: #0a0d14;
border-color: transparent; font-weight: 600; padding: 10px 12px;
}
.btn-resolve:hover { background: #7ab4fb; color: #0a0d14; }
/* ====== Callouts ====== */
.callout {
margin-top: 20px; padding: 14px 16px;
background: var(--bg-page); border: 1px solid var(--border-default);
border-radius: 10px; font-size: 13px; color: var(--text-muted-foreground);
line-height: 1.55; border-left: 3px solid var(--warning);
}
.callout strong { color: var(--text-heading); font-weight: 600; }
/* ====== State detail panels ====== */
.states-title {
margin-top: 48px; font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600; font-size: 18px; color: var(--text-heading);
}
.states-sub { margin-top: 4px; color: var(--text-muted-foreground); font-size: 13px; }
.states {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.state {
background: var(--bg-page); border: 1px solid var(--border-default);
border-radius: 10px; overflow: hidden;
display: flex; flex-direction: column;
}
.state-label {
padding: 10px 14px; border-bottom: 1px solid var(--border-default);
font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600; font-size: 12.5px; color: var(--text-heading);
background: var(--bg-sidebar);
}
.state-body {
padding: 0; background: var(--bg-page);
min-height: 280px;
display: flex; flex-direction: column; justify-content: flex-end;
position: relative;
}
.state-mini-chat {
flex: 1; padding: 14px 16px;
font-size: 11px; color: var(--text-muted);
display: flex; align-items: flex-end; gap: 6px;
font-family: 'JetBrains Mono', monospace;
opacity: 0.6;
}
.mini-composer {
border-top: 1px solid var(--border-default);
padding: 10px 14px; display: flex; gap: 8px; align-items: center;
}
.mini-input {
flex: 1; background: var(--bg-card);
border: 1px solid var(--border-default); border-radius: 8px;
padding: 7px 10px; font-size: 11.5px; color: var(--text-muted);
}
.mini-send {
width: 28px; height: 28px; border-radius: 7px;
background: var(--accent); color: #0a0d14; border: 0; font-size: 14px;
display: flex; align-items: center; justify-content: center;
}
.state-caption {
padding: 10px 14px 12px; font-size: 11.5px;
color: var(--text-muted-foreground); line-height: 1.5;
border-top: 1px solid var(--border-default); background: var(--bg-sidebar);
}
.state-caption strong { color: var(--text-heading); font-weight: 600; }
/* ====== Escalate intercept popover ====== */
.intercept-wrap {
position: relative;
padding: 24px 14px 14px;
background: var(--bg-page);
flex: 1;
display: flex;
align-items: flex-end;
justify-content: flex-start;
}
.intercept-popover {
position: absolute;
bottom: 70px;
left: 14px;
width: 340px;
background: var(--bg-card);
border: 1px solid var(--border-hover);
border-radius: 10px;
padding: 14px;
box-shadow: 0 18px 40px rgba(0,0,0,0.55), 0 0 0 1px rgba(96,165,250,0.15);
}
.intercept-popover::after {
content: '';
position: absolute;
bottom: -7px; left: 40px;
width: 14px; height: 14px;
background: var(--bg-card);
border-right: 1px solid var(--border-hover);
border-bottom: 1px solid var(--border-hover);
transform: rotate(45deg);
}
.intercept-head {
font-family: 'Bricolage Grotesque', sans-serif;
font-weight: 600; font-size: 13px; color: var(--text-heading);
margin-bottom: 4px;
}
.intercept-sub {
font-size: 12px; color: var(--text-muted-foreground);
line-height: 1.5; margin-bottom: 12px;
}
.intercept-options {
display: flex; flex-direction: column; gap: 6px;
}
.intercept-option {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-radius: 8px;
background: var(--bg-elevated); border: 1px solid var(--border-default);
font-size: 12.5px; color: var(--text-primary);
cursor: pointer; text-align: left; width: 100%;
transition: border-color 0.12s, background-color 0.12s;
font-family: inherit;
}
.intercept-option:hover { border-color: var(--border-hover); background: var(--bg-sidebar); }
.intercept-option.primary {
border-color: var(--danger-border); background: var(--danger-dim);
}
.intercept-option.primary:hover { border-color: var(--danger); background: var(--danger-dim-strong); }
.intercept-kbd {
margin-left: auto; font-size: 10.5px; color: var(--text-muted);
font-family: 'JetBrains Mono', monospace;
background: rgba(148,163,184,0.08);
padding: 2px 6px; border-radius: 4px;
}
.mock-btn-row {
display: flex; gap: 8px;
padding: 12px 14px 14px;
border-top: 1px solid var(--border-default);
}
.mock-escalate {
background: transparent; color: var(--text-muted-foreground);
border: 1px solid var(--border-default); padding: 9px 14px;
border-radius: 8px; font-size: 12.5px; min-width: 96px;
position: relative;
}
.mock-escalate.active {
border-color: var(--danger-border); color: var(--danger);
background: var(--danger-dim);
}
.mock-resolve {
flex: 1; background: var(--accent); color: #0a0d14;
border: 0; font-weight: 600; padding: 9px 12px;
border-radius: 8px; font-size: 12.5px;
}
/* Partial inline input row */
.partial-note {
margin-top: 4px;
padding: 6px 10px;
background: rgba(103, 232, 249, 0.08);
border: 1px solid var(--info-border);
border-radius: 6px;
font-size: 12px; color: var(--text-primary);
display: flex; align-items: center; gap: 8px;
font-style: italic;
}
.partial-note-label {
font-style: normal; color: var(--info);
font-size: 10.5px; font-weight: 700; letter-spacing: 0.6px;
text-transform: uppercase;
}
.lane-body::-webkit-scrollbar,
.chat-scroll::-webkit-scrollbar { width: 6px; }
.lane-body::-webkit-scrollbar-thumb,
.chat-scroll::-webkit-scrollbar-thumb { background: var(--border-hover); border-radius: 3px; }
</style>
</head>
<body>
<div class="page">
<div class="page-header">
<div class="page-title">Post-apply outcome states — how we recognize whether a fix worked</div>
<div class="page-sub">
Hero frame shows the <strong style="color:var(--text-primary)">Verifying</strong> state — what the banner becomes the moment the engineer clicks Apply. Below, four detail panels show the other outcome paths: <strong style="color:var(--text-primary)">Partial apply</strong>, <strong style="color:var(--text-primary)">AI-inferred outcome</strong> from chat, <strong style="color:var(--text-primary)">Escalate-intercept</strong>, and the <strong style="color:var(--text-primary)">Nudge</strong> that appears when the engineer keeps chatting without confirming.
</div>
</div>
<!-- ============ HERO: VERIFYING ============ -->
<div class="frame">
<div class="chat">
<div class="chat-head">
<div>
<div class="chat-head-title">Outlook won't authenticate after tenant migration</div>
<div class="chat-head-sub">ticket #48213 · in progress · 14:26</div>
</div>
</div>
<div class="chat-scroll">
<div class="msg ai">
<div class="msg-av">AI</div>
<div>
<div class="msg-body">Given Credential Manager still has entries for the prior tenant, the cleanest path is to clear those and rebuild the local Outlook profile.</div>
<div class="msg-meta">14:22</div>
</div>
</div>
<div class="msg user">
<div>
<div class="msg-body">Okay, I'll run the script now.</div>
<div class="msg-meta">14:24</div>
</div>
<div class="msg-av">ME</div>
</div>
<div class="msg system">
<div class="msg-av"></div>
<div>
<div class="msg-body">Applied fix: Clear cached credentials + rebuild Outlook profile — script completed without errors at 14:24.</div>
</div>
</div>
</div>
<!-- VERIFY BANNER (persistent after Apply) -->
<div class="composer-wrap">
<div class="banner banner-verify" role="region" aria-label="Verify fix outcome">
<div class="banner-icon">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</div>
<div class="banner-body">
<div class="banner-head">
<span>Verifying</span>
<span class="pill">Applied 14:24 · 2m ago</span>
</div>
<div class="banner-title">Did "Clear cached credentials + rebuild Outlook profile" work?</div>
<div class="banner-note">Mark the outcome so the AI can either close the session with this as the resolution, or propose something else.</div>
</div>
<div class="banner-actions">
<button class="btn btn-ghost" aria-label="More options" title="Mark partial apply, re-open details">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/></svg>
</button>
<button class="btn btn-danger-outline">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
Didn't work
</button>
<button class="btn btn-success">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
It worked
</button>
</div>
</div>
<div class="composer">
<div class="composer-input">Tell the AI what happened — or click an outcome above</div>
<button class="composer-send" aria-label="Send">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
</div>
</div>
</div>
<!-- Task lane: fix is now in "verifying" status — no longer a standalone suggested fix -->
<div class="lane">
<div class="lane-head">
<div class="lane-head-label">Task lane</div>
</div>
<div class="lane-body">
<section>
<div class="section-label">
<span class="dot dot-accent"></span>
What we know
<span class="section-meta">· 5 facts</span>
</div>
<div class="fact">
<div class="fact-title">User cannot authenticate to Outlook; repeated 401s from Exchange Online.</div>
<div class="fact-meta">promoted 14:02 · from ticket</div>
</div>
<div class="fact">
<div class="fact-title">Credential Manager still references the prior tenant from six months ago.</div>
<div class="fact-meta">promoted 14:07 · from chat</div>
</div>
<div class="fact">
<div class="fact-title">Teams + SharePoint work on same workstation — isolated to Outlook.</div>
<div class="fact-meta">promoted 14:22 · from chat</div>
</div>
</section>
</div>
<div class="action-bar">
<button class="btn btn-escalate">Escalate</button>
<button class="btn btn-resolve">Resolve</button>
</div>
</div>
</div>
<div class="callout">
<strong>How Verifying works.</strong> Clicking Apply transitions the banner into this state instead of dismissing it. No timeout — the banner stays pinned until the engineer marks <em>Worked</em>, <em>Didn't work</em>, or <em>Partial</em> (overflow). If they ignore it and keep chatting, the Nudge state (panel D below) appears after a few messages. If they hit the task lane's <em>Resolve</em> button without clicking either outcome, we auto-stamp <code style="font-family:'JetBrains Mono',monospace;font-size:11.5px;background:var(--bg-card);padding:1px 5px;border-radius:3px;">applied_success</code>. If they hit <em>Escalate</em>, panel C intercepts.
</div>
<!-- ============ DETAIL PANELS ============ -->
<div class="states-title">Outcome branches</div>
<div class="states-sub">Four paths from Verifying to a final status. Each one writes to <code style="font-family:'JetBrains Mono',monospace;font-size:12px;background:var(--bg-card);padding:1px 6px;border-radius:3px;color:var(--text-primary)">session_suggested_fixes.status</code> so the AI's next turn has ground truth about what's been tried.</div>
<div class="states">
<!-- A. PARTIAL -->
<div class="state">
<div class="state-label">A. Partial apply — "I did some of it"</div>
<div class="state-body">
<div class="state-mini-chat">…engineer picked "Mark partial…" from the verify banner's overflow menu</div>
<div class="banner banner-partial">
<div class="banner-icon">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
</div>
<div class="banner-body">
<div class="banner-head">
<span>Partially applied</span>
<span class="pill">Parked</span>
</div>
<div class="banner-title">Clear cached credentials + rebuild Outlook profile</div>
<div class="partial-note">
<span class="partial-note-label">Note</span>
<span>Ran cred clear — skipped profile rebuild, user in a meeting. Back at 3:30.</span>
</div>
</div>
<div class="banner-actions">
<button class="btn btn-ghost">Edit note</button>
<button class="btn btn-danger-outline">Didn't work</button>
<button class="btn">Finish it </button>
</div>
</div>
<div class="mini-composer">
<div class="mini-input">Type a message…</div>
<button class="mini-send"></button>
</div>
</div>
<div class="state-caption">
<strong>Status:</strong> <code style="font-family:'JetBrains Mono',monospace;font-size:11.5px">applied_partial</code>, with <code style="font-family:'JetBrains Mono',monospace;font-size:11.5px">partial_notes</code> free-text. Not terminal — banner stays pinned until engineer marks a terminal outcome, or clicks <em>Finish it</em> to re-run the remainder and flip back to Verifying. AI treats partial as "tried but uncertain" — doesn't re-propose, but doesn't assume failure either.
</div>
</div>
<!-- B. AI-INFERRED CONFIRM -->
<div class="state">
<div class="state-label">B. AI-inferred outcome — from chat</div>
<div class="state-body">
<div class="state-mini-chat" style="flex-direction:column;align-items:flex-end;gap:8px;opacity:0.8">
<div style="background:var(--bg-card);border:1px solid var(--border-default);border-radius:10px;padding:8px 12px;font-size:12px;color:var(--text-heading);font-style:normal;font-family:inherit;max-width:80%;"><strong style="font-weight:500">Engineer:</strong> "yep that fixed it, thanks"</div>
<div style="font-size:10.5px;color:var(--text-muted);padding-right:2px;">14:31 · user message triggered <code style="font-family:'JetBrains Mono',monospace;font-size:10.5px">[FIX_OUTCOME]</code></div>
</div>
<div class="banner banner-ai">
<div class="banner-icon">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/></svg>
</div>
<div class="banner-body">
<div class="banner-head">
<span>AI detected outcome</span>
<span class="pill">Success · 92%</span>
</div>
<div class="banner-title">AI thinks the fix resolved the issue — confirm?</div>
<div class="banner-note">Based on your message at 14:31. One click closes the session with this fix as the documented resolution.</div>
</div>
<div class="banner-actions">
<button class="btn btn-ghost">Not yet</button>
<button class="btn btn-danger-outline">No, didn't work</button>
<button class="btn btn-success">Confirm · Resolve</button>
</div>
</div>
<div class="mini-composer">
<div class="mini-input">Type a message…</div>
<button class="mini-send"></button>
</div>
</div>
<div class="state-caption">
<strong>Triggered by</strong> the new <code style="font-family:'JetBrains Mono',monospace;font-size:11.5px">[FIX_OUTCOME fix_id=… outcome=success]</code> marker from the system prompt. Engineer stays in the loop — the AI <em>proposes</em> the outcome, doesn't set it. One-click accept fires the normal Resolve flow. Works for failure too ("still broken" → <em>No, didn't work</em> pre-selected, with the AI's reasoning shown).
</div>
</div>
<!-- C. ESCALATE INTERCEPT -->
<div class="state">
<div class="state-label">C. Escalate-intercept — capture outcome before handoff</div>
<div class="state-body">
<div class="intercept-wrap">
<div class="intercept-popover">
<div class="intercept-head">Before escalating — what happened with the fix?</div>
<div class="intercept-sub">"Clear cached credentials" is still in the Verifying state. Tag its outcome so the senior picking this up knows what's been tried.</div>
<div class="intercept-options">
<button class="intercept-option primary">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--danger)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
The fix didn't work
<span class="intercept-kbd"></span>
</button>
<button class="intercept-option">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
It worked — escalating for another reason
</button>
<button class="intercept-option">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
Never actually applied it
</button>
</div>
</div>
</div>
<div class="mock-btn-row">
<button class="mock-escalate active">Escalate</button>
<button class="mock-resolve">Resolve</button>
</div>
</div>
<div class="state-caption">
<strong>Fires when</strong> engineer clicks Escalate while a fix is in Verifying (or Partial). Defaults to <em>Didn't work</em> on Enter — common case. <em>Escalating for another reason</em> preserves success; <em>Never applied</em> flips to <code style="font-family:'JetBrains Mono',monospace;font-size:11.5px">dismissed</code>. Takes 1s and makes the escalation narrative honest for whoever picks it up.
</div>
</div>
<!-- D. NUDGE -->
<div class="state">
<div class="state-label">D. Nudge — passive prompt after a few messages</div>
<div class="state-body">
<div class="state-mini-chat" style="flex-direction:column;align-items:flex-end;gap:6px;opacity:0.8;">
<div style="background:var(--bg-card);border:1px solid var(--border-default);border-radius:10px;padding:7px 11px;font-size:11.5px;color:var(--text-heading);font-style:normal;font-family:inherit;max-width:70%;">"user is rebooting"</div>
<div style="background:var(--bg-card);border:1px solid var(--border-default);border-radius:10px;padding:7px 11px;font-size:11.5px;color:var(--text-heading);font-style:normal;font-family:inherit;max-width:75%;">"okay it's back up, signing in now"</div>
<div style="background:var(--bg-card);border:1px solid var(--border-default);border-radius:10px;padding:7px 11px;font-size:11.5px;color:var(--text-heading);font-style:normal;font-family:inherit;max-width:75%;">"going to try opening Outlook"</div>
</div>
<div class="banner banner-nudge">
<svg class="nudge-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><path d="M12 16h.01"/></svg>
<span class="nudge-title">Did <strong style="color:var(--text-heading)">"Clear cached credentials"</strong> work?</span>
<button class="btn btn-ghost" style="padding:4px 10px">Still checking</button>
<button class="btn btn-danger-outline" style="padding:4px 10px">No</button>
<button class="btn btn-success" style="padding:4px 10px">Yes</button>
</div>
<div class="mini-composer">
<div class="mini-input">Type a message…</div>
<button class="mini-send"></button>
</div>
</div>
<div class="state-caption">
<strong>Appears after</strong> ~3 post-apply engineer messages with no outcome click. Collapses the verify banner into this thin nudge strip above it so chat space isn't eaten. Passive — never auto-marks anything. <em>Still checking</em> silences the nudge for another 3 messages. Yes/No route to the normal Success / Failed flows.
</div>
</div>
</div>
</div>
</body>
</html>