Files
tessera-ctl/.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-PLAN.md
T
schalli c294bfddf2
Tessera CI/CD / Lint & Type Check (push) Successful in 57s
Tessera CI/CD / Tests (push) Successful in 1m54s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 20s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m22s
docs(quick-260923-le6): Proxmox-Abnahmebefunde behoben (PMG-Teilsumme, Aktualisieren nur fuer Admins)
Uebergabe-Notizen der pausierten Sitzung entfernt, die Arbeit ist wieder aufgenommen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 15:35:30 +02:00

23 KiB
Raw Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, estimate, must_haves
phase plan type wave depends_on files_modified autonomous requirements estimate must_haves
quick 260923-le6 execute 1
apps/api/src/proxmox/proxmox-normalize.ts
apps/api/src/proxmox/proxmox-normalize.spec.ts
apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx
apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx
apps/web/src/messages/de.json
apps/web/src/messages/en.json
apps/web/src/app/(portal)/modules/proxmox/page.tsx
apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx
true
tokens raw_tokens tasks confidence
45000 45000 3 low
truths artifacts key_links
PMG: fehlt von einem Paar (spamcount_in/spamcount_out bzw. viruscount_in/viruscount_out) genau eine Haelfte, ist spamCount bzw. virusCount null (Anzeige 'unbekannt') — in beide Richtungen, nie eine Teilsumme
PMG: sind beide Haelften vorhanden, bleibt die Summe wie bisher (10+2 -> 12); fehlen beide, bleibt null
Auf der Proxmox-Modulseite sehen nur ADMIN und SUPER_ADMIN den Knopf 'Jetzt aktualisieren'; USER (und ein noch nicht geladener Benutzer) sehen ihn nicht
Ein noch nie abgefragter Server zeigt Admins weiterhin 'Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.'; Nicht-Admins sehen stattdessen einen Text ohne Verweis auf den Knopf
Sonst aendert sich an der Modulseite nichts (Festlegung: kein Umbau, keine zusaetzlichen Details/Statusfarben)
path provides
apps/api/src/proxmox/proxmox-normalize.ts sumOrNull liefert null, sobald ein Teilwert null ist
path provides
apps/api/src/proxmox/proxmox-normalize.spec.ts Testfaelle 'nur eine Haelfte vorhanden -> null' fuer Spam und Viren, beide Richtungen
path provides
apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx optionale Eigenschaft isAdmin (Vorgabe false), waehlt den Hinweistext fuer noch nie abgefragte Server
path provides
apps/web/src/app/(portal)/modules/proxmox/page.tsx Aktualisieren-Knopf nur fuer Admins, reicht isAdmin an ServerCard weiter
path provides
apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx Seitentest: Knopf sichtbar fuer ADMIN/SUPER_ADMIN, unsichtbar fuer USER/null
from to via pattern
apps/web/src/app/(portal)/modules/proxmox/page.tsx ServerCard isAdmin={isAdmin} isAdmin={isAdmin}
from to via pattern
apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx apps/web/src/messages/de.json proxmox.card.notPolledYetAutomatic t('card.notPolledYetAutomatic') card.notPolledYetAutomatic
Zwei Befunde aus der Abnahme des Proxmox-Moduls beheben, sonst nichts:
  1. PMG-Teilsumme (API): sumOrNull(a, b) in apps/api/src/proxmox/proxmox-normalize.ts addiert heute einen fehlenden Teilwert als 0, sobald nur EINE Haelfte null ist. Dadurch zeigt die Seite z. B. „Spam: 10“ als vollstaendige Tageszahl, obwohl spamcount_out fehlte. Das ist der in .planning/quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/260923-dhh-VERIFICATION.md (Wahrheit 7, Blocker) belegte Fehler. Kuenftig ist die Summe null, sobald ein Teilwert null ist.
  2. Aktualisieren-Knopf nur fuer Admins (Web): Der Knopf „Jetzt aktualisieren“ erscheint heute bei allen, die Zugriff auf das Modul haben. Der Endpunkt POST servers/:id/poll verlangt aber @Roles(Role.ADMIN, Role.SUPER_ADMIN) (apps/api/src/proxmox/proxmox.controller.ts:89-90), deshalb passiert beim Klick fuer alle anderen nichts. Kuenftig sehen nur ADMIN/SUPER_ADMIN den Knopf. Der Hinweis „Noch keine Abfrage gelaufen. Klicken Sie oben auf …“ darf Nicht-Admins nicht mehr auf einen Knopf verweisen, den sie nicht sehen.

Festlegung (locked, vom Nutzer): KEIN Umbau der Proxmox-Modulseite. Der Nutzer hat seinen Wunsch nach mehr Details bzw. Statusfarben ausdruecklich zurueckgezogen. Nur diese zwei Korrekturen, keine weiteren Anzeige-, Layout- oder Textaenderungen.

Hinweis zum Zuschnitt: Tracer-first entfaellt (wie --no-tracer). Es handelt sich um zwei voneinander unabhaengige Fehlerkorrekturen, jede in genau einer Schicht, ohne neue Architektur, die ein Durchstich absichern muesste. Aufgabe 1 (API) und Aufgabe 2/3 (Web) beruehren keine gemeinsamen Dateien. Aufgabe 3 braucht die Eigenschaft isAdmin aus Aufgabe 2.

Purpose: Das Modul soll keinen still falschen, plausibel aussehenden Wert zeigen (eigener Anspruch in proxmox-normalize.ts und ServerCard.tsx: „ein still falscher Wert waere schlimmer als ein ehrliches unbekannt“). Ausserdem soll kein Knopf erscheinen, der fuer den Betrachter wirkungslos ist. Output: korrigierte sumOrNull samt Tests; ServerCard mit isAdmin-Eigenschaft und einem zweiten Hinweistext in de/en; Modulseite, die den Knopf nur Admins zeigt, samt neuem Seitentest.

<execution_context> @/.claude/gsd-core/workflows/execute-plan.md @/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/STATE.md @./CLAUDE.md @.planning/quick/260923-dhh-proxmox-modul-pve-pbs-und-pmg-anbinden-n/260923-dhh-VERIFICATION.md

@apps/api/src/proxmox/proxmox-normalize.ts @apps/api/src/proxmox/proxmox-normalize.spec.ts @apps/web/src/app/(portal)/modules/proxmox/page.tsx @apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx @apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx @apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx

Bereits im Code vorhanden (vom Planer gelesen, nicht erneut suchen):
  • apps/web/src/lib/stores/auth-store.ts: useAuthStore((s) => s.user), user.role ist 'SUPER_ADMIN' | 'ADMIN' | 'USER'.
  • page.tsx berechnet BEREITS const isAdmin = user?.role === 'ADMIN' || user?.role === 'SUPER_ADMIN'; (Zeile 18-19) und nutzt es fuer den Einstellungs-Link. Das ist das etablierte Muster; es wird kein neuer Mechanismus eingefuehrt.
  • apps/web/src/lib/proxmox-api.ts: listServers(): Promise<ProxmoxServer[]>, pollServer(id: string): Promise<ProxmoxTestResult>, Typ ProxmoxServer (inkl. isActive, pollIntervalMin, status: ProxmoxServerStatus | null).
  • ServerCard wird ausschliesslich in page.tsx:99 verwendet (per grep geprueft).
  • Test-Muster fuer Rollen: settings-roles.test.tsx mockt @/lib/stores/auth-store mit useAuthStore: (selector) => mockAuthStore(selector) und setzt je Fall mockAuthStore.mockImplementation((sel) => sel({ user })), ausserdem next/link als <a> und next-intl mit handgeschriebener Uebersetzungstabelle.
  • Nachrichtendateien: nur apps/web/src/messages/de.json und apps/web/src/messages/en.json. Namensraum proxmox.card (de.json ab Zeile 712). umlaut-guard.spec.ts prueft de.json auf Ersatzschreibungen (ae/oe/ue/ss) — neue deutsche Texte brauchen echte Umlaute.
  • Abfragetakt: ProxmoxServer.pollIntervalMin Vorgabe 5, erlaubt 1–1440 (dto/proxmox-server.dto.ts @Min(1) @Max(1440)). Der Planer laeuft je Mandant im kleinsten Intervall der aktiven Server (proxmox-scheduler.service.ts). Inaktive Server (isActive: false) werden nicht automatisch abgefragt.
Aufgabe 1: PMG-Summe wird null, sobald eine Haelfte fehlt (sumOrNull) apps/api/src/proxmox/proxmox-normalize.ts, apps/api/src/proxmox/proxmox-normalize.spec.ts apps/api/src/proxmox/proxmox-normalize.ts (Zeilen 222-270), apps/api/src/proxmox/proxmox-normalize.spec.ts (Zeilen 185-240) - Nur `spamcount_in: 10` vorhanden, `spamcount_out` fehlt -> `spamCount` ist `null` (heute faelschlich 10) - Nur `spamcount_out: 2` vorhanden, `spamcount_in` fehlt -> `spamCount` ist `null` - Nur `viruscount_in: 1` vorhanden, `viruscount_out` fehlt -> `virusCount` ist `null` - Nur `viruscount_out: 3` vorhanden, `viruscount_in` fehlt -> `virusCount` ist `null` - Eine Haelfte vorhanden, die andere ist nicht lesbar (z. B. `spamcount_out: 'abc'`, `readNumber` liefert null) -> `spamCount` ist `null` - Unabhaengigkeit der Paare: Spam unvollstaendig, Viren vollstaendig (`viruscount_in: 1, viruscount_out: 0`) -> `spamCount` null, `virusCount` 1; `countIn`/`countOut` bleiben unberuehrt - Unveraendert gruen: die bestehenden Tests „beide vorhanden -> 12/1“, „beide fehlen -> null“, „HTML -> antwortform“ RED: Im bestehenden `describe('normalizePmg (Aufgabe 3, )', ...)`-Block von `proxmox-normalize.spec.ts` neue Faelle fuer jede Zeile aus `` ergaenzen. Das geht als einzelne `it` oder als `it.each` ueber eine Tabelle {Beschreibung, data, erwartetes spamCount, erwartetes virusCount}. Jeder Testname nennt „nur eine Haelfte vorhanden -> null“ und die Richtung (in bzw. out) sowie Spam bzw. Viren. Vorhandene Tests unveraendert lassen. Der Planer hat geprueft, dass keiner das alte Verhalten festschreibt: Die vorhandenen PMG-Tests decken nur „beide vorhanden“ und „beide fehlen“ ab, und `proxmox.service.spec.ts:350-360` liefert beide Haelften (`spamcount_in: 1, spamcount_out: 0`). Test ausfuehren, die neuen Faelle muessen ROT sein. Commit `test(260923-le6): PMG-Teilsumme ohne Haelfte muss null sein`.

GREEN: sumOrNull(a, b) so aendern, dass es null zurueckgibt, sobald a ODER b null ist. Nur wenn beide Zahlen sind, wird ihre Summe zurueckgegeben. Die bisherige Ersatz-durch-Null-Addition entfaellt vollstaendig, ein fehlender Teilwert wird nie mehr als 0 behandelt. Ueber der Funktion einen kurzen deutschen Kommentar ergaenzen (Stil der Datei, ASCII-Umschreibungen wie im Rest der Datei): Eine Tageszahl aus zwei Teilwerten ist nur dann bekannt, wenn beide Teilwerte bekannt sind; eine Teilsumme saehe vollstaendig aus, waere aber still falsch (Abnahmebefund 260923-dhh, Wahrheit 7; PMG-Feldnamen sind nur Annahme A5). normalizePmg selbst und die Feldtabelle PMG_STATS_FIELDS bleiben unveraendert. Tests muessen GRUEN sein. Commit fix(260923-le6): PMG-Summe null bei fehlendem Teilwert. pnpm --filter api exec vitest run src/proxmox test "$(grep -v '^\s*//' apps/api/src/proxmox/proxmox-normalize.ts | grep -c '?? 0) + (')" -eq 0 pnpm --filter api type-check Alle Tests unter apps/api/src/proxmox gruen, darunter mindestens 5 neue Faelle „nur eine Haelfte vorhanden -> null“ (Spam in/out, Viren in/out, nicht lesbare Haelfte). Die Ersatz-durch-Null-Addition steht nicht mehr in proxmox-normalize.ts. API-Typpruefung ohne Fehler.

Aufgabe 2: ServerCard waehlt den Hinweistext nach Rolle (isAdmin) und zweiter Text in de/en apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx, apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx (Zeilen 150-215), apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx, apps/web/src/messages/de.json (Zeilen 706-720), apps/web/src/messages/en.json (Zeilen 706-720) - `isAdmin` gesetzt, Server nie abgefragt (`status.lastPolledAt === null`) -> Text „Noch keine Abfrage gelaufen. Klicken Sie oben auf „Jetzt aktualisieren“.“ (wie heute) - `isAdmin={false}`, Server nie abgefragt -> Text „Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.“, und nirgends in der Karte steht „Jetzt aktualisieren“ - `isAdmin` weggelassen -> verhaelt sich wie `isAdmin={false}` (sichere Vorgabe) - Weiterhin in keinem der Faelle die Sammelmeldung „Unerwarteter Fehler.“ Umsetzung der zweiten Korrektur, Teil Karte.

(a) Nachrichten: In apps/web/src/messages/de.json unter proxmox.card, direkt nach notPolledYet, den neuen Schluessel notPolledYetAutomatic mit dem Wert „Noch keine Abfrage gelaufen. Die Werte erscheinen nach der nächsten automatischen Abfrage.“ anlegen. Echtes „ä“ verwenden (umlaut-guard), Sie-Form bzw. unpersoenlich wie die uebrigen App-Texte. In apps/web/src/messages/en.json an derselben Stelle notPolledYetAutomatic: „No poll has run yet. The values will appear after the next automatic poll.“ Andere Schluessel nicht anfassen; notPolledYet, refresh und refreshing bleiben unveraendert. Begruendung der Wortwahl (Planer-Ermessen, Vorschlag aus dem Auftrag angepasst): Ein „in Kürze“ waere nicht immer wahr, denn das Intervall ist je Server von 1 bis 1440 Minuten einstellbar (@Max(1440)). „Nach der nächsten automatischen Abfrage“ stimmt bei jedem Intervall und verweist auf keinen Knopf.

(b) ServerCard.tsx: ServerCardProps um die optionale Eigenschaft isAdmin?: boolean erweitern und in der Funktionssignatur mit Vorgabe false entgegennehmen. Die sichere Vorgabe bedeutet: Wer die Eigenschaft vergisst, zeigt keinen Verweis auf einen Knopf. Im vorhandenen Zweig fuer nie abgefragte Server (status && !status.lastPolledAt) den Text nach isAdmin waehlen. Ist isAdmin wahr, bleibt der heutige Aufruf t('card.notPolledYet', { refreshLabel: t('card.refresh') }) unveraendert, sonst t('card.notPolledYetAutomatic'). Den Kommentar „Nachbesserung Befund 2“ um einen Satz ergaenzen: Nicht-Admins sehen den Knopf nicht (der Poll-Endpunkt verlangt ADMIN/SUPER_ADMIN) und bekommen deshalb den Text ohne Knopfverweis (260923-le6). Sonst NICHTS an der Karte aendern, auch keine Formatierung unbeteiligter Zeilen (Festlegung: kein Umbau). Insbesondere kein biome format --write auf die ganze Datei, das wuerde unbeteiligte Zeilen umbrechen.

(c) ServerCard.test.tsx: In die next-intl-Mock-Tabelle 'card.notPolledYetAutomatic' mit dem deutschen Text aus (a) aufnehmen. Den bestehenden Test „Nachbesserung Befund 2: …“ auf render(<ServerCard server={server} isAdmin />) umstellen; seine Erwartungen bleiben. Neue Tests fuer die Faelle aus <behavior> ergaenzen: isAdmin={false} sowie weggelassenes isAdmin jeweils mit Erwartung des automatischen Textes, queryByText(/Jetzt aktualisieren/) ist null und queryByText('Unerwarteter Fehler.') ist null. Zuerst die Tests schreiben und ROT sehen, dann (a)+(b) umsetzen und GRUEN sehen. Ein Commit genuegt: fix(260923-le6): Proxmox-Karte verweist Nicht-Admins nicht auf den Aktualisieren-Knopf. pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx" src/messages node -e "const d=require('./apps/web/src/messages/de.json'),e=require('./apps/web/src/messages/en.json');const a=d.proxmox.card.notPolledYetAutomatic,b=e.proxmox.card.notPolledYetAutomatic;if(!a||!b||/aktualisieren/i.test(a)||/refresh/i.test(b)||!a.includes('nächsten'))process.exit(1);if(d.proxmox.card.notPolledYet!=='Noch keine Abfrage gelaufen. Klicken Sie oben auf „{refreshLabel}“.')process.exit(2)" ServerCard-Tests gruen (bestehende und neue Admin-/Nicht-Admin-Faelle); src/messages-Tests (umlaut-guard, Paritaet) gruen. notPolledYetAutomatic existiert in de und en und erwaehnt keinen Aktualisieren-Knopf. notPolledYet ist unveraendert.

Aufgabe 3: Modulseite zeigt „Jetzt aktualisieren“ nur ADMIN/SUPER_ADMIN, mit Seitentest apps/web/src/app/(portal)/modules/proxmox/page.tsx, apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx apps/web/src/app/(portal)/modules/proxmox/page.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx (Zeilen 1-100, nur das Mock-Muster) - Rolle USER, eine Serverliste mit einem nie abgefragten Server -> kein Knopf mit Namen „Jetzt aktualisieren“; der Karten-Hinweis ist der automatische Text - Kein Benutzer geladen (`user: null`) -> kein Knopf - Rolle ADMIN -> Knopf „Jetzt aktualisieren“ sichtbar; der Karten-Hinweis ist der Admin-Text mit Knopfverweis - Rolle SUPER_ADMIN -> Knopf sichtbar - Leere Serverliste bei ADMIN -> weiterhin kein Knopf (bestehende Bedingung `servers.length > 0` bleibt) Umsetzung der zweiten Korrektur, Teil Seite.

(a) Neue Testdatei apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx nach dem Muster von settings-roles.test.tsx anlegen. Gemockt werden @/lib/proxmox-api (listServers als vi.fn(), der je Fall eine Liste aufloest, und pollServer als vi.fn()), @/lib/stores/auth-store (Selektor-Durchreichung ueber mockAuthStore), next/link (als <a>) und next-intl. Die handgeschriebene Uebersetzungstabelle enthaelt mindestens title, description, loading, loadError, emptyState, card.refresh, card.refreshing, card.settingsLink, card.unknownValue, card.lastPolledLabel, card.notPolledYet (mit {refreshLabel}-Ersetzung wie in ServerCard.test.tsx) und card.notPolledYetAutomatic. Die Seite ueber import ProxmoxPage from './page' rendern. Mit waitFor/findByText auf den Servernamen warten, weil listServers asynchron ist. Danach Knopf per queryByRole('button', { name: 'Jetzt aktualisieren' }) bzw. getByRole pruefen. Ein Testfall je Zeile aus <behavior>; der Server im Test ist ein nie abgefragter Server (Status wie im Befund-2-Test von ServerCard.test.tsx: lastPolledAt: null, reachable: false, errorKind: null). afterEach mit cleanup() und vi.clearAllMocks(). Test ausfuehren, die USER- und null-Faelle muessen ROT sein.

(b) page.tsx: Die bestehende Bedingung des Aktualisieren-Knopfs (servers !== null && servers.length > 0) zusaetzlich an isAdmin knuepfen, sodass der Knopf nur fuer ADMIN/SUPER_ADMIN gerendert wird. Die vorhandene Variable isAdmin wiederverwenden, keinen neuen Rollen-Mechanismus einfuehren. handleRefresh bleibt unveraendert. An der Render-Stelle <ServerCard server={server} /> die Eigenschaft isAdmin={isAdmin} weiterreichen. Den Kopfkommentar der Komponente um einen Satz ergaenzen: Der Knopf erscheint nur fuer Admins, weil POST servers/:id/poll @Roles(ADMIN, SUPER_ADMIN) verlangt; fuer andere waere er wirkungslos (260923-le6). Sonst nichts an der Seite aendern: keine neuen Texte, kein Layout, keine Import-Umsortierung. Das vorbestehende organizeImports-Signal von biome in dieser Datei bleibt unangetastet.

Tests GRUEN sehen. Commit fix(260923-le6): Aktualisieren-Knopf der Proxmox-Seite nur fuer Admins. pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" grep -c 'isAdmin={isAdmin}' "apps/web/src/app/(portal)/modules/proxmox/page.tsx" pnpm --filter web type-check Alle Web-Tests im Proxmox-Verzeichnis gruen (ServerCard, ServerForm, neuer Seitentest mit mindestens 5 Faellen: USER, null, ADMIN, SUPER_ADMIN, leere Liste). page.tsx reicht isAdmin={isAdmin} an ServerCard weiter. Web-Typpruefung ohne Fehler.

<threat_model>

Trust Boundaries

Boundary Description
Browser -> API POST /proxmox/servers/:id/poll Nicht-Admin koennte die Abfrage manuell ausloesen; die Berechtigung prueft ausschliesslich der Server (@Roles(ADMIN, SUPER_ADMIN))
PMG-Server -> normalizePmg Fremde, nur angenommene Antwortform (Annahme A5); unvollstaendige Felder duerfen keinen falschen Wert erzeugen

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-le6-01 Elevation of Privilege POST servers/:id/poll low accept Das Ausblenden des Knopfes ist reine Oberflaeche, keine Sicherheitsgrenze. Die Durchsetzung bleibt unveraendert serverseitig per @Roles(Role.ADMIN, Role.SUPER_ADMIN) in proxmox.controller.ts:89-90. Dieser Plan aendert den Controller nicht.
T-le6-02 Tampering (Integritaet der Anzeige) sumOrNull in normalizePmg medium mitigate Aufgabe 1: Summe null, sobald ein Teilwert fehlt oder unlesbar ist. Die neuen Tests decken beide Richtungen fuer Spam und Viren ab.
T-le6-03 Information Disclosure ServerCard Hinweistext low accept Der neue Text enthaelt keine Server- oder Zugangsdaten, nur einen statischen Hinweis.
</threat_model>
Nach allen drei Aufgaben (vom Planer an der Ausgangslage 6530ae5 geprueft: alles gruen, `biome lint` sauber):
  • pnpm --filter api exec vitest run src/proxmox gruen
  • pnpm --filter web exec vitest run "src/app/(portal)/modules/proxmox" src/messages gruen
  • pnpm --filter api type-check und pnpm --filter web type-check ohne Fehler
  • pnpm exec biome lint apps/api/src/proxmox/proxmox-normalize.ts apps/api/src/proxmox/proxmox-normalize.spec.ts "apps/web/src/app/(portal)/modules/proxmox/page.tsx" "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx" "apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.test.tsx" "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx" ohne Befund
  • pnpm exec biome check "apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx" ohne Befund (neue Datei, voll konform)
  • biome check auf den fuenf VORHANDENEN Dateien: vorher 6 Befunde, alle vorbestehend (Formatierung je Datei, dazu organizeImports in page.tsx). Deren Anzahl darf nicht steigen. Die vorbestehenden Befunde werden nicht mit behoben, das waere fremder Diff (Festlegung: kein Umbau).
  • Keine Container-Neubauten, kein Deploy, keine Browserpruefung in diesem Plan

<success_criteria>

  • Eine PMG-Antwort mit nur einer Haelfte eines Spam- oder Viren-Paares ergibt null, die Seite zeigt dort also „unbekannt“ statt einer Teilsumme.
  • Auf der Proxmox-Modulseite sehen nur ADMIN und SUPER_ADMIN „Jetzt aktualisieren“. Der Hinweis fuer nie abgefragte Server verweist Nicht-Admins auf die automatische Abfrage statt auf den Knopf.
  • Sonst keine sichtbare Aenderung an der Modulseite.
  • In der SUMMARY als Beobachtung vermerken, nicht beheben: Inaktive Server (isActive: false) werden nicht automatisch abgefragt. Fuer einen inaktiven, nie abgefragten Server stimmt der neue Nicht-Admin-Text deshalb nicht ganz. Das ist ein vorbestehender Randfall, denn auch die Karte fuer Admins beachtet isActive heute nicht. Er liegt ausserhalb dieses Auftrags (Festlegung: kein Umbau) und wird dem Nutzer zur Entscheidung vorgelegt.
  • In der SUMMARY vermerken, dass damit die offene Luecke (Wahrheit 7) aus 260923-dhh-VERIFICATION.md geschlossen ist. </success_criteria>
Create `.planning/quick/260923-le6-proxmox-abnahmebefunde-sumornull-null-be/260923-le6-SUMMARY.md` when done