refactor(ms-ai-architect): playground v1.14.0 sesjon 5 — phase-rapporter til expansion-list

- renderMigrate: <section class="phase-detail"> per fase erstattet med
  <div class="expansion">-list (DS-supplement). Default-collapsed, klikkbar
  header (Fase N: navn + duration), body = milepaeler + suksesskriterier.
  Behold cycle-ribbon + mat-ladder + phases-summary-tabell + risks-tabell.
- renderPoc: speil renderMigrate. Traffic-light flyttet inn i expansion-body
  (ul.traffic-list per fase med status fra fasens stepState).
- renderSummary: KEY_STATS_CONFIG['verdict'] patchet — parseTable returnerer
  rader med header-baserte nokler (Metric/Verdi/Mal) ikke canonical
  {label,value,unit}. Ny logikk bruker metrics_headers + heuristikk-match for
  label/value/unit-kolonner, med fallback til canonical felt.
  Backward-kompatibelt.
- renderAdr: verifisert PASS — ingen endring (.adr-meta + critique-cards
  rendrer pent uten ekstra arbeid).
- ACTIONS['phase-expand']: ny handler registrert som alias for
  requirement-expand (samme toggle-monster, eget action-navn for senere
  divergens).
- Lokal CSS: hele .phase-detail-blokken (~10 linjer) slettet. Defensive-
  kommentar oppsummert til 5-linjers historie-notat.
- Style-blokk effektive linjer: 147 (var 178 etter sesjon 4).

Smoke-tester:
- validate-plugin.sh: 219 PASS
- run-e2e.sh --playground: 272 PASS (202 statisk + 70 parser)
- test-playground-migrations.sh: 7 PASS

Refs V1.14.0-AUDIT.local.md sub-batch D.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-08 20:36:25 +02:00
commit 30ddeb2d9f

View file

@ -205,22 +205,11 @@
- .expansion__title-main/sub (display: block)
- .matrix__bubble (cursor + hover + focus) */
/* v1.13.1 fix (B12, B13, B15): defensive block-layout for sibling-rapport-
seksjoner som kan ha overflow-issues mot foregående grid-elementer
(.small-multiples, .kanban-board, .mat-ladder). Sikrer eksplisitt
block-flow + clear så de ikke leker grid-celler eller flyter.
v1.14.0 sesjon 3: .top-risks fjernet (ligger nå i .card med riktig DS block-flow).
v1.14.0 sesjon 4: .aiact-timeline + .kanban-board + .report-meta + .suppressed-panel
fjernet (kanban-board → aiact-timeline-section / details.suppressed-panel stacker
naturlig som block-level <section>/<details>; .report-meta-mønsteret er borte). */
.phase-detail,
.mat-ladder + .phase-detail,
.phase-detail + .phase-detail { display: block; clear: both; width: 100%; }
.phase-detail { margin-top: var(--space-5); }
.phase-detail h3 { margin-bottom: var(--space-3); }
.phase-detail h4 { margin: var(--space-3) 0 var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
.phase-detail ul { padding-left: var(--space-5); margin: 0 0 var(--space-2); }
.phase-detail ul li { margin-bottom: var(--space-1); }
/* v1.14.0 sesjon 5: .phase-detail-CSS slettet — renderMigrate + renderPoc
bruker nå <div class="expansion">-list (DS-supplement) i stedet for det
lokale .phase-detail-mønsteret. v1.13.1-defensive layout-overrides for
.top-risks (sesjon 3), .aiact-timeline + .report-meta + .suppressed-panel
(sesjon 4) og .phase-detail (sesjon 5) er nå alle ute. */
</style>
</head>
<body>
@ -4199,15 +4188,29 @@
const phasesSummaryHtml = phasesSummaryRows
? '<table class="report-table"><thead><tr><th>Fase</th><th>Varighet</th><th>Milepæler</th><th>Suksesskriterier</th><th>Status</th></tr></thead><tbody>' + phasesSummaryRows + '</tbody></table>'
: '';
const detailsHtml = phases.map(function (p) {
// v1.14.0 sesjon 5: phase-detail (lokal CSS-mønster) erstattet med
// <div class="expansion">-list (DS-supplement). Default-collapsed, klikkbar
// header = fase-navn + varighet, body = milepæler + suksesskriterier.
// phase-expand-handler er registrert i ACTIONS som alias for samme
// toggle-mønster som requirement-expand.
const detailsHtml = phases.length ? '<div class="stack-sm">' + phases.map(function (p, idx) {
const ms = (p.milestones || []).map(function (m) { return '<li>' + escapeHtml(m) + '</li>'; }).join('');
const sc = (p.success_criteria || []).map(function (s) { return '<li>' + escapeHtml(s) + '</li>'; }).join('');
return '<section class="phase-detail">' +
'<h3>' + escapeHtml(p.name) + ' <small>(' + (p.duration_weeks || '?') + ' uker)</small></h3>' +
(ms ? '<h4>Milepæler</h4><ul>' + ms + '</ul>' : '') +
(sc ? '<h4>Suksesskriterier</h4><ul>' + sc + '</ul>' : '') +
'</section>';
}).join('');
const innerBody = (ms ? '<h4>Milepæler</h4><ul>' + ms + '</ul>' : '') +
(sc ? '<h4>Suksesskriterier</h4><ul>' + sc + '</ul>' : '');
return '<div class="expansion" aria-expanded="false">' +
'<button type="button" class="expansion__head" data-action="phase-expand" data-idx="' + idx + '">' +
'<span class="expansion__title">' +
'<span class="expansion__title-main">Fase ' + (idx + 1) + ': ' + escapeHtml(p.name || '—') + '</span>' +
'<span class="expansion__title-sub">' + escapeHtml(String(p.duration_weeks || '—')) + ' uker</span>' +
'</span>' +
'<span class="expansion__chev" aria-hidden="true"></span>' +
'</button>' +
'<div class="expansion__body"><div class="expansion__body-inner"><div>' +
innerBody +
'</div></div></div>' +
'</div>';
}).join('') + '</div>' : '';
const risksRows = (data.risks || []).map(function (r) {
return '<tr><td>' + escapeHtml(r.risk || '') + '</td><td>' + escapeHtml(r.probability || '') + '</td><td>' + escapeHtml(r.consequence || '') + '</td><td>' + escapeHtml(r.mitigation || '') + '</td></tr>';
}).join('');
@ -4393,9 +4396,11 @@
const phasesSummaryHtml = phasesSummaryRows
? '<table class="report-table"><thead><tr><th>Fase</th><th>Varighet</th><th>Milepæler</th><th>Suksesskriterier</th><th>Status</th></tr></thead><tbody>' + phasesSummaryRows + '</tbody></table>'
: '';
// B5 traffic-light per success-kriterie. R15: uten eksplisitt status,
// bruk fasens state — done=go, active=warning, future=neutral.
const detailsHtml = phases.map(function (p, i) {
// v1.14.0 sesjon 5: phase-detail erstattet med expansion-list (samme
// mønster som renderMigrate). Traffic-light per success-kriterie beholdes
// inne i expansion-body. R15: uten eksplisitt status, bruk fasens state —
// done=green, active=yellow, future=gray.
const detailsHtml = phases.length ? '<div class="stack-sm">' + phases.map(function (p, i) {
const state = stepStateFor(p, i);
const tlStatus = state === 'completed' ? 'green' : state === 'current' ? 'yellow' : 'gray';
const ms = (p.milestones || []).map(function (m) { return '<li>' + escapeHtml(m) + '</li>'; }).join('');
@ -4407,12 +4412,21 @@
'</span>' +
'</li>';
}).join('');
return '<section class="phase-detail">' +
'<h3>' + escapeHtml(p.name) + ' <small>(' + (p.duration_weeks || '?') + ' uker)</small></h3>' +
(ms ? '<h4>Milepæler</h4><ul>' + ms + '</ul>' : '') +
(sc ? '<h4>Suksesskriterier</h4><ul class="traffic-list">' + sc + '</ul>' : '') +
'</section>';
}).join('');
const innerBody = (ms ? '<h4>Milepæler</h4><ul>' + ms + '</ul>' : '') +
(sc ? '<h4>Suksesskriterier</h4><ul class="traffic-list">' + sc + '</ul>' : '');
return '<div class="expansion" aria-expanded="false">' +
'<button type="button" class="expansion__head" data-action="phase-expand" data-idx="' + i + '">' +
'<span class="expansion__title">' +
'<span class="expansion__title-main">Fase ' + (i + 1) + ': ' + escapeHtml(p.name || '—') + '</span>' +
'<span class="expansion__title-sub">' + escapeHtml(String(p.duration_weeks || '—')) + ' uker</span>' +
'</span>' +
'<span class="expansion__chev" aria-hidden="true"></span>' +
'</button>' +
'<div class="expansion__body"><div class="expansion__body-inner"><div>' +
innerBody +
'</div></div></div>' +
'</div>';
}).join('') + '</div>' : '';
const risksRows = (data.risks || []).map(function (r) {
return '<tr><td>' + escapeHtml(r.risk || '') + '</td><td>' + escapeHtml(r.probability || '') + '</td><td>' + escapeHtml(r.consequence || '') + '</td><td>' + escapeHtml(r.mitigation || '') + '</td></tr>';
}).join('');
@ -4736,11 +4750,24 @@
},
'verdict': function (d) {
const km = d.key_metrics || [];
if (!km.length) return [];
// v1.14.0 sesjon 5: parseTable produserer rader med header-baserte nøkler
// (f.eks. Metric/Verdi/Mål) — ikke canonical {label,value,unit}. Bruk
// metrics_headers + heuristikk for å mappe til key-stat-form, med
// fallback til canonical-felt hvis fixturen er normalisert.
const headers = d.metrics_headers || Object.keys(km[0] || {});
const findKey = function (re) { return headers.find(function (h) { return re.test(h); }); };
const labelKey = findKey(/^(label|name|metric|metrikk|navn)$/i) || headers[0] || 'label';
const valueKey = findKey(/^(value|verdi|score)$/i) || headers[1] || 'value';
const unitKey = findKey(/^(unit|enhet|hint|m[åa]l|target)$/i);
return km.slice(0, 4).map(function (m) {
const labelRaw = m[labelKey] != null ? m[labelKey] : (m.label || m.name || '');
const valueRaw = m[valueKey] != null && m[valueKey] !== '' ? m[valueKey] : (m.value != null ? m.value : '—');
const hintRaw = unitKey ? m[unitKey] : m.unit;
return {
label: String(m.label || m.name || '').toUpperCase(),
value: m.value != null ? m.value : '—',
hint: m.unit || undefined
label: String(labelRaw || '').toUpperCase(),
value: valueRaw,
hint: hintRaw || undefined
};
});
},
@ -5463,6 +5490,11 @@
exp.setAttribute('aria-expanded', open ? 'false' : 'true');
};
// v1.14.0 sesjon 5: phase-expand alias — renderMigrate + renderPoc bruker
// samme expansion-toggle-mønster som requirement-expand, men med eget action-
// navn for å gjøre intent eksplisitt og åpne for senere divergens.
ACTIONS['phase-expand'] = ACTIONS['requirement-expand'];
ACTIONS['onboarding-goto-group'] = function (ev, el) {
const groupId = el.dataset.group;
const root = getSurfaceEl('onboarding');