Add async pipeline with progress monitoring, resumability, and result transparency

Pipeline engine rewritten with combo-first loop: each combination is processed
through all requested passes before moving to the next, with incremental DB
saves after every step (crash-safe). Blocked combos now get result rows so they
appear in the results page with constraint violation reasons.

New pipeline_runs table tracks run lifecycle (pending/running/completed/failed/
cancelled). Web route launches pipeline in a background thread with its own DB
connection. HTMX polling partial shows live progress with per-pass breakdown.

Also: status guard prevents reviewed->scored downgrade, save_combination loads
existing status on dedup for correct resume, per-metric scores show domain
bounds + units + position bars, ensure_metric backfills units on existing rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Simonson, Andrew
2026-02-18 15:30:52 -06:00
parent 8118a62242
commit d2028a642b
17 changed files with 1263 additions and 217 deletions

View File

@@ -154,3 +154,77 @@ dd { font-size: 0.9rem; }
/* ── Dep add form ────────────────────────────────────────── */
.dep-add-form { margin-top: 0.75rem; }
/* ── Form hints ──────────────────────────────────────────── */
.form-hint { color: #666; font-size: 0.8rem; margin-bottom: 0.25rem; font-weight: 400; }
/* ── Vertical checkbox list ──────────────────────────────── */
.checkbox-col { display: flex; flex-direction: column; gap: 0.5rem; }
.checkbox-col label { display: flex; align-items: baseline; gap: 0.4rem; font-size: 0.9rem; }
.checkbox-col label .form-hint { display: block; margin-left: 1.3rem; }
/* ── Summary DL (pipeline) ───────────────────────────────── */
.summary-dl { display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 1rem; }
/* ── Pipeline run status ────────────────────────────────── */
.badge-running { background: #dbeafe; color: #1e40af; }
.badge-completed { background: #dcfce7; color: #166534; }
.badge-failed { background: #fee2e2; color: #991b1b; }
.badge-cancelled { background: #fef3c7; color: #92400e; }
.run-status { padding: 0.25rem 0; }
.run-status-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.run-status-label { font-weight: 600; font-size: 0.9rem; }
.progress-bar-container {
background: #e5e7eb;
border-radius: 4px;
height: 8px;
overflow: hidden;
margin-bottom: 0.35rem;
}
.progress-bar {
background: #2563eb;
height: 100%;
border-radius: 4px;
transition: width 0.3s ease;
}
.run-status-counters {
display: flex;
gap: 1rem;
font-size: 0.8rem;
color: #555;
margin-bottom: 0.35rem;
}
.run-status-actions { margin-top: 0.35rem; }
/* ── Block reason ───────────────────────────────────────── */
.block-reason-cell {
font-size: 0.8rem;
color: #666;
max-width: 350px;
word-break: break-word;
}
/* ── Metric position bar ────────────────────────────────── */
.metric-bar-container {
display: inline-block;
width: 60px;
height: 6px;
background: #e5e7eb;
border-radius: 3px;
overflow: hidden;
vertical-align: middle;
}
.metric-bar {
height: 100%;
background: #2563eb;
border-radius: 3px;
}
.metric-bar-label {
font-size: 0.75rem;
color: #666;
margin-left: 0.3rem;
}