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>
806 lines
35 KiB
HTML
806 lines
35 KiB
HTML
<!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>
|