/** * @fileoverview Problems inbox — the triage surface between external sources * and the task list. * * GoingsOn is the list of solutions. A problem is a *candidate* pulled from * somewhere else (wam tickets, /audit and /fuzz findings) that becomes work only * when promoted. Nothing here creates a problem: they arrive from adapters and * from go-mcp's `report_problems`, because a hand-typed problem would have no * source to reconcile against on the next pull. * * Rows are ranked by painhours, a 0-100 score computed from pain, scale, and * age. It is not stored, so an untriaged problem climbs on its own between one * visit and the next — that is the anti-starvation mechanism, and it is why the * list is worth revisiting rather than draining once. */ (function () { 'use strict'; const esc = GoingsOn.utils.escapeHtml; const escAttr = GoingsOn.utils.escapeAttrValue; /** Current filter state, kept in the module rather than the URL: the inbox * is a working view, not a place you deep-link into. */ let filters = { status: 'Open', source: null }; /** Last loaded rows, so an action can act without a refetch. */ let rows = []; /** Map a painhours band onto the shared badge palette. */ function bandColor(band) { switch (band) { case 'critical': return 'red'; case 'high': return 'yellow'; case 'medium': return 'blue'; default: return 'muted'; } } function statusColor(status) { switch (status) { case 'Promoted': return 'green'; case 'Dismissed': return 'muted'; case 'Resolved': return 'cyan'; default: return 'purple'; } } /** * Load the inbox and render it. */ async function load() { const grid = document.getElementById('problems-list'); if (!grid) return; try { rows = await GoingsOn.api.problems.list({ status: filters.status, source: filters.source, }); } catch (err) { console.error('[problems] load failed:', err); grid.innerHTML = `
Could not load problems.
${esc(what)}
` + `Problems arrive from wam and from audit runs. They are candidates: promote one to make it a task.
` + `