diff --git a/CLAUDE.md b/CLAUDE.md
index a4dcd4a..62b0cbf 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4871,6 +4871,31 @@ Kommentare nach dreimaligem Bereinigen unverändert). `entitiesReparieren()`
`project_kommentare.status` und `protokoll_punkte.text` aller Sitzungen über
`entitiesEntstapeln()` zurück.
+## Behobener Fehler: Kommentare abgewählter Gewerke (11/2026)
+
+Eriks Meldung zu 21-4030: Im Projekt-Dashboard stand ein HLS-Kommentar,
+obwohl das Projekt seit KW 31 nur noch ELT hat. Ursache: Die einmalige
+Umstellung `kommentareInAktuelleSitzungUebernehmen()` hatte je Gewerk den
+jüngsten früheren Kommentar übernommen, ohne die aktuellen Gewerke des
+Projekts zu prüfen; der Sitzungswechsel trug ihn weiter. Jetzt:
+`kommentareDesProjekts()` (js/utils.js) — die Gesamtanzeige
+(`kommentarVon(p, null)`, `kommentarAnzeigeHtml()`) und der Verlauf im
+Projekt-Dashboard (`planKommentarDaten()`, je Sitzung mit DEREN Gewerken)
+zeigen nur Gewerke des Projekts; `ensureCurrentWeekRollover()` übernimmt nur
+Kommentare dieser Gewerke. Gelöscht wird nichts — die verirrte Kopie fällt
+beim nächsten Sitzungswechsel aus der aktuellen Sitzung heraus.
+
+**Darstellung mehrerer Gewerke „B – Überschrift mit Linie“** (Eriks Wahl
+aus dem Artifact „Kommentare je Gewerk“): `kommentarAnzeigeHtml(p, null,
+mitMeta)` zeichnet je Gewerk eine Kopfzeile (Gewerk in seiner Farbe
+`--gewerk-elt/-hls/-arch`, feine Linie, mit `mitMeta` Verfasser und Zeit)
+und darunter den Text; bei nur einem Gewerk wie bisher nur den Text.
+Genutzt von der Kachel „Kommentar der Sitzung“ im Projekt-Dashboard (dort
+entfällt dann die gemeinsame Verfasserzeile, `kommentarGewerkeMitText()`),
+der Kommentaransicht der Projektliste/Wandtafel (`renderKommentarAnsicht()`)
+und den Verlauf-Kacheln (ohne Verfasser). Stile `.kommentar-gewerke`,
+`.kommentar-kopf` in `styles.css`.
+
## Behobene Fehler (Testlauf 10/2026, fünf Test-Agents)
- **Gesperrte Sitzungen sind für Nicht-Admins auch inhaltlich unveränderlich**:
diff --git a/public/App.html b/public/App.html
index 870a9e3..70a3949 100644
--- a/public/App.html
+++ b/public/App.html
@@ -38,7 +38,7 @@
-
+
-
+
diff --git a/public/js/board/board.js b/public/js/board/board.js
index 624f86f..928cb29 100644
--- a/public/js/board/board.js
+++ b/public/js/board/board.js
@@ -748,7 +748,7 @@ function renderBoardDrawer(p, allPeople, locked, weekLabel, opts){
// renderKommentarAnsicht: der Kommentar der laufenden Sitzung, nur zu lesen.
// Parameter: p (Projekt), locked. Rückgabe: HTML-String.
function renderKommentarAnsicht(p, locked){
- const text = sanitizeStatusHtml(kommentarVon(p, kommentarGewerk()).status || '');
+ const text = kommentarAnzeigeHtml(p, kommentarGewerk(), true);
return `
${text ||
'Für diese Sitzung ist noch kein Kommentar erfasst.'}
`;
}
diff --git a/public/js/plan/plan-info.js b/public/js/plan/plan-info.js
index baf3c7a..a83dc6c 100644
--- a/public/js/plan/plan-info.js
+++ b/public/js/plan/plan-info.js
@@ -93,7 +93,7 @@
for(let i = versionen.length - 1; i >= 0; i--){
const k = kommentarVon(versionen[i].project, null);
if(statusPlainText(k.status)){
- letzter = { week: versionen[i].week, k, aktuell: i === versionen.length - 1 };
+ letzter = { week: versionen[i].week, k, project: versionen[i].project, aktuell: i === versionen.length - 1 };
break;
}
}
@@ -103,7 +103,8 @@
gewerke.forEach(g => {
let vorher = '';
versionen.forEach(v => {
- const k = kommentarVon(v.project, g);
+ // Nur, wenn das Projekt in JENER Sitzung das Gewerk hatte.
+ const k = (g in kommentareDesProjekts(v.project)) ? kommentarVon(v.project, g) : { status: '' };
const text = statusPlainText(k.status);
if(text && text !== vorher && !(letzter && v.week === letzter.week)){
verlauf.push({ week: v.week, gewerk: g, html: sanitizeStatusHtml(k.status), autor: k.statusAuthor || '', at: k.statusAt || '' });
@@ -184,8 +185,9 @@
if(editEl){
editEl.className = 'status-edit is-ansicht';
editEl.contentEditable = 'false';
- // Alle Gewerke zusammen, je mit Gewerk davor (kommentarVon(), js/utils.js).
- const text = kom.letzter ? sanitizeStatusHtml(kom.letzter.k.status) : '';
+ // Alle Gewerke zusammen; bei mehreren je mit Überschrift, Linie und
+ // Verfasser (kommentarAnzeigeHtml(), js/utils.js, Eriks Wahl „B“).
+ const text = kom.letzter ? kommentarAnzeigeHtml(kom.letzter.project, null, true) : '';
editEl.innerHTML = text || 'Zu diesem Projekt ist noch kein Kommentar erfasst.';
}
const hinweisEl = document.getElementById('planStatusHinweis');
@@ -200,7 +202,9 @@
const autorEl = document.getElementById('planStatusAutor');
if(autorEl){
const k = kom.letzter ? kom.letzter.k : {};
- autorEl.innerHTML = k.status && k.statusAuthor
+ // Bei mehreren Gewerken steht der Verfasser schon je Gewerk im Text.
+ const mehrere = kom.letzter && kommentarGewerkeMitText(kom.letzter.project) > 1;
+ autorEl.innerHTML = !mehrere && k.status && k.statusAuthor
? `${esc(initialsForName(k.statusAuthor))}${esc(k.statusAuthor)}`
+ (k.statusAt && typeof formatCommentStamp === 'function' ? ', ' + esc(formatCommentStamp(k.statusAt)) : '')
: '';
diff --git a/public/js/utils.js b/public/js/utils.js
index 09ad2e6..1f62aa4 100644
--- a/public/js/utils.js
+++ b/public/js/utils.js
@@ -385,8 +385,21 @@ function kommentarGewerk(){
// Verfasser/Zeit des jüngsten, besprochen nur, wenn ALLE Gewerke des
// Projekts es abgehakt haben. Parameter: p (Projekt), g (Gewerk oder
// null/undefined). Rückgabe: { status, statusAuthor, statusAt, besprochen }.
-function kommentarVon(p, g){
+// kommentareDesProjekts: die Kommentare nur der Gewerke, die das Projekt hat.
+// Ein Kommentar eines Gewerks, das dem Projekt nicht (mehr) gehört — etwa aus
+// einer Zeit, als es noch HLS hatte —, gehört nicht in die Gesamtanzeige
+// (Eriks Meldung zu 21-4030). Ohne gepflegte Gewerke: alle. Rückgabe: Objekt.
+function kommentareDesProjekts(p){
const alle = (p && p.kommentare) || {};
+ const gewerke = Array.isArray(p && p.gewerke) ? p.gewerke : [];
+ if(!gewerke.length) return alle;
+ const aus = {};
+ Object.keys(alle).forEach(k => { if(gewerke.includes(k)) aus[k] = alle[k]; });
+ return aus;
+}
+
+function kommentarVon(p, g){
+ const alle = g ? ((p && p.kommentare) || {}) : kommentareDesProjekts(p);
if(g){
const e = alle[g] || {};
return { status: e.status || '', statusAuthor: e.statusAuthor || '', statusAt: e.statusAt || '', besprochen: !!e.besprochen };
@@ -408,19 +421,37 @@ function kommentarVon(p, g){
}
// kommentarAnzeigeHtml: der Kommentar zur ANZEIGE, bereinigt. Mit Gewerk g
-// nur dessen Text; ohne Gewerk die Texte aller Gewerke, bei mehreren je mit
-// dem Gewerk als eigener, dezenter Beschriftung (.kommentar-gewerk) —
-// Systemtext, nicht Teil des Kommentars (anders als die fette "ELT:"-Zeile,
-// die kommentarVon() für Klartext-Vorschauen einfügt). Rückgabe: HTML ('' ohne Text).
-function kommentarAnzeigeHtml(p, g){
+// nur dessen Text; ohne Gewerk die Texte aller Gewerke des Projekts. Bei
+// mehreren Gewerken Darstellung „B – Überschrift mit Linie“ (Eriks Wahl):
+// je Gewerk eine Kopfzeile mit dem Gewerk in seiner Farbe, einer feinen Linie
+// und (mitMeta) Verfasser und Zeit, darunter der Text — Systemtext, nicht
+// Teil des Kommentars (anders als die fette "ELT:"-Zeile, die kommentarVon()
+// für Klartext-Vorschauen einfügt). Parameter: p, g, mitMeta.
+// Rückgabe: HTML ('' ohne Text).
+function kommentarAnzeigeHtml(p, g, mitMeta){
if(g) return sanitizeStatusHtml(kommentarVon(p, g).status || '');
- const alle = (p && p.kommentare) || {};
+ const alle = kommentareDesProjekts(p);
const reihenfolge = (typeof DEPARTMENTS === 'object' && DEPARTMENTS) ? Object.keys(DEPARTMENTS) : ['ELT','HLS','ARCH'];
Object.keys(alle).forEach(k => { if(!reihenfolge.includes(k)) reihenfolge.push(k); });
const mitText = reihenfolge.filter(k => alle[k] && statusPlainText(alle[k].status) !== '');
if(mitText.length === 0) return '';
if(mitText.length === 1) return sanitizeStatusHtml(alle[mitText[0]].status);
- return mitText.map(k => `