From 8876408079ce6acffb10f86788aa819fe874642f Mon Sep 17 00:00:00 2001 From: DYAI2025 Date: Sat, 29 Aug 2026 01:45:02 +0200 Subject: [PATCH 1/2] chore(repo): EYT-148 Sprint-7-UI/UX-Abnahmevertrag als Repo-Regel mit CI-Waechter verankern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Sprint-7-Abnahmevertrag (reale Journeys statt Mocks, Human-only-PO-Gate, Golden-Baseline-Schutz, READY_FOR_PO_VISUAL_REVIEW als hoechster Agentenstatus) lebte bisher nur im Chat-Kontext einzelner Claude-Sessions. Jetzt: - .claude/rules/sprint-7-ui-ux-acceptance.md: vollstaendiger Vertrag (ACTIVE_TEMPORARY, Sprint ID 579, EYT-148, Removal Condition). - CLAUDE.md: kurzer Anker mit @-Import — keine Volltextkopie. - apps/api/test/sprint7-acceptance-rule-guardrails.test.ts: Waechter im Pflichtjob unit-tests; prueft Marker, Abschnitte und Substanz-Untergrenzen auf kommentar-/fence-bereinigtem Text, keinen Byte-Snapshot. - turbo.json: Rule, CLAUDE.md und die Handoff-Artefakte in die test-Inputs — sonst replayt ein lokaler Turbo-Lauf nach Entkernung der Rule einen alten gruenen Cache (CI war nie betroffen, dort gibt es keinen Turbo-Cache). - .gitignore: /.claude/settings.local.json — .claude/ ist hiermit erstmals teilweise getrackt in einem oeffentlichen Repo. Co-Authored-By: Claude Fable 5 --- .claude/rules/sprint-7-ui-ux-acceptance.md | 173 +++++++++++++++ .gitignore | 6 + CLAUDE.md | 19 ++ ...sprint7-acceptance-rule-guardrails.test.ts | 198 ++++++++++++++++++ turbo.json | 9 +- 5 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 .claude/rules/sprint-7-ui-ux-acceptance.md create mode 100644 apps/api/test/sprint7-acceptance-rule-guardrails.test.ts diff --git a/.claude/rules/sprint-7-ui-ux-acceptance.md b/.claude/rules/sprint-7-ui-ux-acceptance.md new file mode 100644 index 0000000..616ea2e --- /dev/null +++ b/.claude/rules/sprint-7-ui-ux-acceptance.md @@ -0,0 +1,173 @@ +# Sprint 7 UI/UX Acceptance Rule + +## Status und Lebenszyklus + +- Status: **ACTIVE_TEMPORARY** +- Applies to: EasyTree Jira Sprint 7, **Sprint ID 579** +- Primary acceptance issue: **EYT-148** („Sprint 7: Admin- und + Mitarbeiter-User-Journeys vollständig mit Playwright und realer Persistenz + abnehmen") +- Wächter: `apps/api/test/sprint7-acceptance-rule-guardrails.test.ts` + (Pflichtjob `unit-tests`) — Entfernen, Umbenennen oder wesentliches + Abschwächen dieser Datei macht CI rot. + +**Removal Condition:** Diese Regel darf erst entfernt oder archiviert werden, nachdem Sprint 7 in Jira formal PO-abgenommen und geschlossen ist und alle wiederverwendbaren Anforderungen in dauerhafte Frontend-Qualitätsregeln überführt wurden. + +## Quellen-Autorität (SSoT) + +Diese Regel ist eine **Ausführungsregel für Coding Agents** — sie ist KEINE +neue Produkt-SSoT und ersetzt weder Jira noch Confluence. + +- **Jira** (Projekt `EYT`, Board 72) besitzt Sprint-, Scope- und + Acceptance-Wahrheit. +- **Confluence** besitzt die Design-/Architektur-Baselines: Seite `8814623` + („EasyTree – Basisdesign v2.0: Werkbank & Feld – ruhig verdichtet") und + Seite `8486960` („EasyTree – Zwei-Client-Architektur und Admin-Kalender"). +- **GitHub** (`DYAI2025/EasyTree`, `origin/master`) besitzt Code-, Branch- und + CI-Wahrheit. +- **Runtime-Evidenz** besitzt Deployment- und Runtime-Wahrheit. +- **Coding-Agent-Ausgaben sind keine SSoT.** + +Vor jeder Sprint-7-Arbeit den aktuellen Jira-/GitHub-/Confluence-Stand frisch +lesen; Auftragsprämissen veralten vor der Ausführung. + +## Product Truth + +Sprint 7 liefert finale produktive Oberflächen: Admin/Werkbank **Desktop-first**, +Mitarbeiter/Feld **Mobile-Web-first**. + +Nicht zulässig als Produktabnahme: Clickdummies, Fake-Daten, Mockserver, +Netzwerk-Fixtures als Ersatz für reale Journeys, Placeholder-Screens, +synthetische Erfolgsmeldungen, LocalStorage-/Clientzustände als operative +Wahrheit. Nicht implementierte Backend-/Domainfähigkeiten dürfen nicht durch UI +vorgetäuscht werden. + +## Bestehende Testarchitektur + +Vor Änderungen mindestens inspizieren: `apps/web/e2e/`, +`apps/web/playwright.config.ts`, `apps/web/playwright.harness.config.ts`, +`apps/web/e2e/auth-journey/`. + +Der einfache `web-smoke` ohne echte API/DB ist kein vollständiger +Sprint-7-Abnahmenachweis. Für Acceptance-Journeys ist die vorhandene +Real-Stack-Infrastruktur zu bevorzugen: + +Browser → Next/Web → reale API → reale Authentifizierung → PostgreSQL/RLS + +Keine zweite parallele E2E-Testarchitektur aufbauen, solange die vorhandene +erweiterbar ist. + +## Test-per-Increment + +Jede tatsächlich veränderte produktive Oberfläche muss, soweit für den Slice +relevant, beweisen: (1) reale Nutzerhandlung; (2) erwartetes sichtbares +Ergebnis; (3) echte autorisierte API-/Domain-Ausführung; (4) Reload bestätigt +Serverzustand; (5) bei relevanten gemeinsamen Zuständen zusätzlicher +Browserkontext; (6) Negativ-/Rechtereise; (7) finale Darstellung im +Basisdesign v2.0; (8) Responsive-Verhalten; (9) Accessibility; +(10) Screenshot aus der real ausgeführten Journey. + +EYT-148 ist das finale aggregierte Gate, aber die Tests dürfen nicht bis zum +Sprintende aufgeschoben werden. + +## Viewports + +- Admin: Chromium `1440 × 900` und `1920 × 1080`. +- Mitarbeiter: Chromium Touch `320 × 800` und `375 × 812`. +- Sprint-Closeout zusätzlich gezielte WebKit-Smokes (Mitarbeiter `375 × 812`, + Admin `1440 × 900`), soweit die vorhandene Infrastruktur das robust zulässt. +- Keine unnötige vollständige Browsermatrix bauen. +- Reproduzierbare Acceptance-Läufe: Locale `de-DE`, Timezone `Europe/Berlin`. + +## Accessibility + +Für tatsächlich verwendete produktive Flächen prüfen: axe A/AA; +Tastaturbedienbarkeit; sichtbarer Fokus; sinnvolle semantische Reihenfolge; +Status nie ausschließlich über Farbe; kein unkontrolliertes horizontales +Seitenscrolling; mobile Hauptaktionen mindestens 56 px; Desktop-Aktionen +mindestens 40 px; keine erforderliche Hover-only-Bedienung auf Mobile; +`prefers-reduced-motion`; Klick-/Tastaturalternative für Drag-and-Drop. + +**200-%-Zoom:** Ein automatisierter Reflow-Test ist zulässig, darf aber NICHT +als identisch mit realem Browserzoom bezeichnet werden. Der finale +PO-Acceptance-Report muss einen echten menschlichen 200-%-Zoom-Check als +Reviewpunkt enthalten. + +## Zustände (States) + +Wo der reale Vertrag den Zustand erzeugen kann: Loading, Empty, Error, +Forbidden, Unauthenticated, Stale, Partial, Retry. `Offline-read-only` nur +dort, wo eine reale fachliche Offline-Lesefähigkeit existiert — keine +Offline-Funktion erfinden, nur damit ein UI-State gezeigt werden kann. + +## Visual Regression + +Screenshots müssen aus realen Produktjourneys stammen. Keine Figma-, Penpot-, +Storybook- oder Mockup-Screenshots als Ersatz für Produktabnahme. + +Verbindlicher Ablauf: + +candidate screenshot → READY_FOR_PO_VISUAL_REVIEW → explicit human PO +approval → accepted golden baseline + +Eine neue oder geänderte Golden Baseline benötigt explizite menschliche PO-Freigabe; ein automatisches Baseline-Update (etwa via --update-snapshots) ist verboten. Coding Agents dürfen einen Visual-Test niemals durch ein +Baseline-Update „reparieren". + +## Browser Integrity + +In positiven Journeys relevante Fehler überwachen: `pageerror`, unerwartete +`console.error`, unerwartete HTTP 5xx, fehlgeschlagene kritische Ressourcen, +Hydrationfehler, unerwartete Weiterleitungen. Negative Journeys müssen +erwartete 401/403- bzw. Forbidden-Zustände explizit beweisen. + +## Autorisierung + +Eine sichtbare Navigation oder Route verleiht niemals Rechte. Insbesondere: +Mitarbeiter erhalten keine Admin-/Kosten-/fremden Organisationsdaten; ohne +`costs.read` keine Kostenbeträge oder Kostensätze; Kosten-Navigation und +Kosteninhalte gemäß EYT-113 nur nach verifiziertem Recht. Serverseitige +Autorisierung bleibt bindend. + +## EYT-148 Final Journey Gate + +Der finale Sprint-7-Kandidat muss mindestens beweisen — + +**Admin:** reales Login → zulässige Admin-Shell → Woche/Planung → realen +Einsatz anlegen → bestätigten Serverzustand sehen → Reload → +Konflikt-/Fehlerfall → konfliktfreien Plan veröffentlichen → veröffentlichten +Stand lesen → mit `costs.read` Kosten/Snapshot lesen → ohne `costs.read` kein +erfolgreicher Kosten-Zugriff. + +**Mitarbeiter:** reales Login → Feld-Shell → Heute → ausschließlich eigene +autorisierte reale Daten → Woche, soweit der reale Employee-Read-Pfad +implementiert ist → Reload → gleicher Serverzustand → kein +Admin-/Kosten-/Fremddatenzugriff. + +Noch nicht implementierte Fachfunktionen dürfen nicht als Fake-Journey ergänzt +werden. + +## PO Acceptance Evidence + +Für den finalen Sprint-Kandidaten muss ein Evidence Package erzeugbar sein, +mindestens mit: `manifest.json`, `PO-ABNAHME.md`, Playwright-Ergebnis, +relevanten Server-/Plan-/Snapshot-IDs, Admin-Screenshots, +Mitarbeiter-Screenshots, State-Screenshots, Accessibility-Ergebnissen, +Visual-Diff-Ergebnissen, Traces bei Fehlern, Video der kanonischen +Admin-Journey, Video der kanonischen Mitarbeiter-Journey. + +Das Manifest bindet mindestens: Git SHA, getestete Umgebung, Browser und +Version, Viewport, Rolle, Zeitpunkt, relevante Server-IDs, +Testdaten-/Seed-Bezug. Große Laufartefakte nicht ungeprüft dauerhaft ins +Repository committen; bevorzugt CI-Artefakte. + +## Human Gate + +Coding Agents dürfen technische Ergebnisse als PASS melden. Coding Agents +dürfen NICHT selbst setzen oder behaupten: „PO approved", „UX accepted", +„UI accepted", „design accepted", „Sprint 7 accepted". + +Ein Coding Agent kann keine PO-/UI-/UX-Abnahme erzeugen; die visuelle und UX-seitige Sprintfreigabe ist Human-/PO-only. + +Der höchste Agenten-Endstatus vor menschlicher Designabnahme lautet: + +READY_FOR_PO_VISUAL_REVIEW diff --git a/.gitignore b/.gitignore index 907a4f7..0e3d7a4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,9 @@ _sprint2-transfer/ .open-next/ .wrangler/ .wrangler-dry/ + +# Claude-Code-Sitzungskonfiguration. Das Repo ist oeffentlich, und mit +# .claude/rules/ ist .claude/ erstmals teilweise getrackt: ohne diesen Eintrag +# veroeffentlicht ein `git add .claude` jeden spaeter in settings.local.json +# abgelegten Token. Dieselbe Falle wie penpot/ weiter oben. +/.claude/settings.local.json diff --git a/CLAUDE.md b/CLAUDE.md index 264648e..feb833d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -133,6 +133,25 @@ before acting on it. Do **not** use local `master` as the reference — compare plus one shared deterministic validator. **EYT-91** is the seed-UUID bug. Publish itself is explicitly deferred out of Sprint 4 ("Bewusst später" in EYT-50). +## Sprint 7 — Binding UI/UX Acceptance Gate (temporary) + +Until Jira Sprint 7 (Sprint ID 579) is formally PO-accepted and closed, +`.claude/rules/sprint-7-ui-ux-acceptance.md` is binding for all Sprint-7 +frontend/UI/UX/browser/accessibility/Playwright/visual-regression work. The +rule file contains the full executable contract; it is loaded into every +session via the import below. + +@.claude/rules/sprint-7-ui-ux-acceptance.md + +Key governance: acceptance journeys run the real Auth → API → PostgreSQL/RLS +path — no mocks, clickdummies or placeholders as product acceptance; EYT-148 +is the final integrated UI/journey gate; Claude may report technical PASS but +can never create human PO acceptance — the highest agent state is +`READY_FOR_PO_VISUAL_REVIEW`; golden visual baselines require explicit PO +approval (never an automatic `--update-snapshots`); re-read the current +Jira/GitHub/Confluence state before acting. Guarded by +`apps/api/test/sprint7-acceptance-rule-guardrails.test.ts`. + ## Commands Node 22 (`.nvmrc`), pnpm 10.28.0 via corepack. **pnpm is the only permitted package manager.** diff --git a/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts b/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts new file mode 100644 index 0000000..163e803 --- /dev/null +++ b/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts @@ -0,0 +1,198 @@ +/** + * Wächter für den Sprint-7-UI/UX-Abnahmevertrag (EYT-148, Sprint 579). + * + * Läuft im Pflichtjob `unit-tests` (`pnpm test` -> `turbo run test`) — bewusst + * kein eigener CI-Job, damit das Ruleset nicht neu angewendet werden muss + * (Muster EYT-46/EYT-89). + * + * Anlass: Der Sprint-7-Abnahmevertrag (reale Journeys statt Mocks, Human-only- + * PO-Gate, Golden-Baseline-Schutz) lebte bisher nur in Chat-Kontext einzelner + * Claude-Sessions. Diese Suite bindet ihn an das Repository: sie wird rot, wenn + * die Rule-Datei fehlt, CLAUDE.md sie nicht mehr referenziert oder einer der + * semantisch tragenden Marker entfernt wird. + * + * Geprüft werden Struktur und Marker, NICHT der Wortlaut ganzer Absätze: + * redaktionelle Verbesserungen bleiben möglich, das Verschwinden einer + * Schutzregel nicht. Wie in `handoff-guardrails.test.ts` steht vor den + * Inhaltszusicherungen eine Nicht-Leerlauf-Zusicherung. + * + * Bewusste Grenzen und Eigenheiten: + * - Eine semantische Umkehrung eines Markers (der wörtlich zitierte Marker + * in einem verneinenden Satz) bleibt BY DESIGN unerkannt: Diese Suite + * prüft Struktur und Marker, nicht Bedeutung — dieselbe Philosophie wie + * `handoff-guardrails.test.ts`. + * - Die drei langen Marker-Sätze stehen in der Rule ABSICHTLICH einzeilig + * (Byte-Identität mit den hier konkatenierten Markern). Ein künftiges + * `proseWrap`-Setting in `.prettierrc.json` würde sie hart umbrechen und + * diese Suite rot machen — dieses Rot bedeutet Formatierung, nicht + * Inhaltsverlust. + * - HTML-Kommentare und eingezäunte Codeblöcke werden vor Struktur- und + * Markersuche entfernt: ein Marker, der nur dort steht, hält den Guard + * nicht grün; allein die globale Längenuntergrenze liest den Rohtext. + * + * Lebenszyklus: Diese Suite darf erst entfernt werden, wenn die Removal + * Condition der Rule erfüllt ist (Sprint 7 formal PO-abgenommen und in Jira + * geschlossen); siehe `.claude/rules/sprint-7-ui-ux-acceptance.md`. + */ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { findRepoRoot } from "./architecture/scan"; + +const repoRoot = findRepoRoot(process.cwd()); +const RULE_PATH = ".claude/rules/sprint-7-ui-ux-acceptance.md"; +const ruleAbs = join(repoRoot, RULE_PATH); + +/** Semantisch tragende Marker. Jeder steht für eine eigene Schutzregel. */ +const MARKER_SPRINT = "Sprint ID 579"; +const MARKER_GATE_ISSUE = "EYT-148"; +const MARKER_FINAL_STATE = "READY_FOR_PO_VISUAL_REVIEW"; +const MARKER_HUMAN_GATE = + "Ein Coding Agent kann keine PO-/UI-/UX-Abnahme erzeugen; " + + "die visuelle und UX-seitige Sprintfreigabe ist Human-/PO-only."; +const MARKER_BASELINE_BAN = + "Eine neue oder geänderte Golden Baseline benötigt explizite menschliche " + + "PO-Freigabe; ein automatisches Baseline-Update (etwa via --update-snapshots) " + + "ist verboten."; +const MARKER_REAL_STACK = + "Browser → Next/Web → reale API → reale Authentifizierung → PostgreSQL/RLS"; +const MARKER_REMOVAL = + "Diese Regel darf erst entfernt oder archiviert werden, nachdem Sprint 7 in " + + "Jira formal PO-abgenommen und geschlossen ist und alle wiederverwendbaren " + + "Anforderungen in dauerhafte Frontend-Qualitätsregeln überführt wurden."; + +/** Pflichtabschnitte — das Fehlen eines Abschnitts hebt eine Zusicherung auf. */ +const REQUIRED_SECTIONS = [ + "Status und Lebenszyklus", + "Quellen-Autorität (SSoT)", + "Product Truth", + "Bestehende Testarchitektur", + "Test-per-Increment", + "Viewports", + "Accessibility", + "Zustände (States)", + "Visual Regression", + "Browser Integrity", + "Autorisierung", + "EYT-148 Final Journey Gate", + "PO Acceptance Evidence", + "Human Gate", +] as const; + +function readRule(): string { + if (!existsSync(ruleAbs)) { + throw new Error( + `${RULE_PATH} fehlt. Der Sprint-7-Abnahmevertrag (EYT-148) ist damit ` + + "unverankert; CLAUDE.md verweist ins Leere. Datei wiederherstellen — " + + "entfernen ist erst nach erfüllter Removal Condition zulässig.", + ); + } + return readFileSync(ruleAbs, "utf8"); +} + +function headings(markdown: string): string[] { + return markdown + .split("\n") + .filter((line) => line.startsWith("## ")) + .map((line) => line.slice(3).trim()); +} + +/** + * Entfernt HTML-Kommentare und eingezäunte Codeblöcke. Was nur dort steht, + * ist für Leser der Regel unsichtbar bzw. Zitat — es darf weder Marker- noch + * Strukturzusicherungen grün halten (Reviewbefund: eine hohle Datei mit allen + * Markern in einem Kommentar bestand die erste Fassung dieser Suite). + */ +function stripNonSemantic(markdown: string): string { + return markdown.replace(//g, "").replace(/```[\s\S]*?```/g, ""); +} + +/** Abschnittskörper: Text nach einer `## `-Überschrift bis zur nächsten. */ +function sectionBodies(markdown: string): Map { + const bodies = new Map(); + let current: string | undefined; + let buffer: string[] = []; + for (const line of markdown.split("\n")) { + if (line.startsWith("## ")) { + if (current !== undefined) { + bodies.set(current, buffer.join("\n")); + } + current = line.slice(3).trim(); + buffer = []; + } else if (current !== undefined) { + buffer.push(line); + } + } + if (current !== undefined) { + bodies.set(current, buffer.join("\n")); + } + return bodies; +} + +/** + * Untergrenze an Nicht-Whitespace-Zeichen je Pflichtabschnitt: grob die + * Hälfte des kleinsten realen Abschnitts (gemessen 29.08.2026: 251 Zeichen, + * „Zustände (States)"), auf einen runden Wert gesetzt. Redaktionelles Kürzen + * bleibt möglich; das Entkernen eines Abschnitts auf eine leere Hülle wird rot. + */ +const SECTION_MIN_NON_WS = 150; + +describe("Sprint-7-UI/UX-Abnahmevertrag (EYT-148)", () => { + const rawRule = readRule(); + const rule = stripNonSemantic(rawRule); + const found = headings(rule); + const bodies = sectionBodies(rule); + + it("hat ueberhaupt Struktur (Nicht-Leerlauf-Zusicherung)", () => { + expect(rawRule.length).toBeGreaterThan(4000); + expect(found.length).toBeGreaterThanOrEqual(REQUIRED_SECTIONS.length); + }); + + it.each(REQUIRED_SECTIONS)("enthaelt den Pflichtabschnitt '%s'", (section) => { + expect(found).toContain(section); + }); + + it.each(REQUIRED_SECTIONS)( + "Pflichtabschnitt '%s' ist nicht entkernt (Substanz-Untergrenze)", + (section) => { + const body = bodies.get(section) ?? ""; + expect(body.replace(/\s+/g, "").length).toBeGreaterThanOrEqual(SECTION_MIN_NON_WS); + }, + ); + + it("bindet die Regel an Sprint 579 und EYT-148 als finales Abnahme-Gate", () => { + expect(rule).toContain(MARKER_SPRINT); + expect(rule).toContain(MARKER_GATE_ISSUE); + expect(found).toContain("EYT-148 Final Journey Gate"); + }); + + it("traegt die Removal Condition (kontrollierte, nicht stille Entfernung)", () => { + expect(rule).toContain(MARKER_REMOVAL); + }); + + it("haelt den hoechsten Agenten-Endstatus vor menschlicher Abnahme fest", () => { + expect(rule).toContain(MARKER_FINAL_STATE); + }); + + it("haelt das Human-/PO-only-Abnahme-Gate fest", () => { + expect(rule).toContain(MARKER_HUMAN_GATE); + }); + + it("verbietet automatische Golden-Baseline-Aktualisierung", () => { + expect(rule).toContain(MARKER_BASELINE_BAN); + }); + + it("verankert den realen Auth->API->PostgreSQL/RLS-Abnahmegrundsatz", () => { + expect(rule).toContain(MARKER_REAL_STACK); + }); + + it("wird von CLAUDE.md als bindend referenziert", () => { + const claudeMd = readFileSync(join(repoRoot, "CLAUDE.md"), "utf8"); + expect(claudeMd).toContain(RULE_PATH); + // Schützt die Bindungsaussage des Ankers vor semantischer Abstufung; + // die @-Import-Zeile selbst bleibt wie im Plan dokumentiert entfernbar. + expect(claudeMd).toContain("is binding for all Sprint-7"); + }); +}); diff --git a/turbo.json b/turbo.json index 6796b04..25baa7f 100644 --- a/turbo.json +++ b/turbo.json @@ -11,7 +11,14 @@ "dependsOn": ["^build"] }, "test": { - "dependsOn": ["^build"] + "dependsOn": ["^build"], + "inputs": [ + "$TURBO_DEFAULT$", + "../../.claude/rules/**", + "../../CLAUDE.md", + "../../docs/handoff/**", + "../../docs/evals/**" + ] } } } From 50d5e81bb0e24d747ba592aaee57aeb8c9c2af04 Mon Sep 17 00:00:00 2001 From: DYAI2025 Date: Sat, 29 Aug 2026 02:17:33 +0200 Subject: [PATCH 2/2] chore(repo): EYT-148 Testintegritaet in Sprint-7-Regel und Waechter nachziehen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewbefund: Die Rule persistierte den EYT-148-Testintegritaetsvertrag unvollstaendig. Nachgezogen als eigener Abschnitt ## Testintegritaet: - Reale Umgebung: isolierte Nicht-Produktionsumgebung mit realer Authentifizierung, API und PostgreSQL/RLS; keine Mock-/Fixture-Antwort als operative Produktwahrheit. - Gegenbeweis: ein absichtlich gebrochener Auth-, Persistenz- oder Routingpfad muss mindestens einen passenden Journey-Test rot machen — kontrolliert, reversibel, nur im Test-/CI-Kontext. - Merge Blocking: Journey-/Accessibility-/Visual-Gates muessen vor finaler EYT-148-Abnahme tatsaechlich merge-blockierend sein; ein freiwilliger CI-Job, den GitHub beim Merge ignorieren koennte, genuegt nicht. Der Abschnitt beschreibt ausdruecklich das erforderliche End-Gate, keinen bereits erfuellten Zustand. - Evidence Binding: Evidenz an exakten Head-SHA, Umgebung, Browser/Version, Rolle und Server-IDs gebunden; keine Uebertragung auf neuere Commits. Waechter: Testintegritaet als Pflichtabschnitt (Praesenz + Substanzgrenze) plus zwei neue Marker-Zusicherungen (Gegenbeweis, Merge-Blocking); beide per Gegenmutation rot gesehen und wiederhergestellt. 36 -> 40 Tests. Co-Authored-By: Claude Fable 5 --- .claude/rules/sprint-7-ui-ux-acceptance.md | 37 +++++++++++++++++++ ...sprint7-acceptance-rule-guardrails.test.ts | 24 ++++++++++++ 2 files changed, 61 insertions(+) diff --git a/.claude/rules/sprint-7-ui-ux-acceptance.md b/.claude/rules/sprint-7-ui-ux-acceptance.md index 616ea2e..faba96e 100644 --- a/.claude/rules/sprint-7-ui-ux-acceptance.md +++ b/.claude/rules/sprint-7-ui-ux-acceptance.md @@ -146,6 +146,43 @@ Admin-/Kosten-/Fremddatenzugriff. Noch nicht implementierte Fachfunktionen dürfen nicht als Fake-Journey ergänzt werden. +## Testintegrität + +### Reale Umgebung + +Pflicht-Acceptance-Journeys laufen gegen eine isolierte +Nicht-Produktionsumgebung mit realer Authentifizierung, realer API und realem +PostgreSQL/RLS. Testdaten dürfen kontrolliert und reproduzierbar sein; die +Anwendung selbst darf keine Mock-/Fixture-Antworten als operative +Produktwahrheit verwenden. + +### Gegenbeweis (Counterexample Proof) + +Ein absichtlich gebrochener Auth-, Persistenz- oder Routingpfad muss mindestens einen passenden Journey-Test rot machen; ein grüner Test ohne nachgewiesene Fähigkeit, den relevanten Defekt zu erkennen, genügt nicht als EYT-148-Abnahmenachweis. + +Die Gegenprobe erfolgt kontrolliert, reversibel und ausschließlich im +Test-/CI-Kontext — niemals durch Mutation einer produktiven Umgebung. + +### Merge Blocking + +Verpflichtende Journey-, Accessibility- und relevante Visual-Regression-Gates müssen vor finaler EYT-148-Abnahme tatsächlich merge-blockierend erzwungen sein; ein grüner freiwilliger CI-Job, den GitHub beim Merge ignorieren könnte, erfüllt dieses Acceptance-Gate nicht. + +Stand heute erzwingt das aktive Ruleset die elf bestehenden CI-Kontexte +(inklusive `unit-tests`, in dem dieser Regel-Wächter läuft). Daraus folgt +NICHT, dass die vollständigen EYT-148-Journey-/Accessibility-/Visual-Gates +bereits existieren: Dieser Abschnitt beschreibt das erforderliche End-Gate für +EYT-148, keinen bereits erfüllten Zustand. Diese Gates werden im Verlauf von +Sprint 7 aufgebaut und sind spätestens vor finalem +`READY_FOR_PO_VISUAL_REVIEW` auf dem exakten Acceptance-Head grün und +merge-blockierend. + +### Evidence Binding + +Acceptance-Evidenz ist an den exakten Git-Head-SHA, die Umgebung, +Browser/Version, die Rolle und die relevanten Server-/Plan-/Snapshot-IDs +gebunden. Evidenz eines älteren Heads wird niemals auf einen neueren Commit +übertragen. + ## PO Acceptance Evidence Für den finalen Sprint-Kandidaten muss ein Evidence Package erzeugbar sein, diff --git a/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts b/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts index 163e803..c2bf5ba 100644 --- a/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts +++ b/apps/api/test/sprint7-acceptance-rule-guardrails.test.ts @@ -62,6 +62,16 @@ const MARKER_REMOVAL = "Diese Regel darf erst entfernt oder archiviert werden, nachdem Sprint 7 in " + "Jira formal PO-abgenommen und geschlossen ist und alle wiederverwendbaren " + "Anforderungen in dauerhafte Frontend-Qualitätsregeln überführt wurden."; +const MARKER_COUNTEREXAMPLE = + "Ein absichtlich gebrochener Auth-, Persistenz- oder Routingpfad muss " + + "mindestens einen passenden Journey-Test rot machen; ein grüner Test ohne " + + "nachgewiesene Fähigkeit, den relevanten Defekt zu erkennen, genügt nicht " + + "als EYT-148-Abnahmenachweis."; +const MARKER_MERGE_BLOCKING = + "Verpflichtende Journey-, Accessibility- und relevante " + + "Visual-Regression-Gates müssen vor finaler EYT-148-Abnahme tatsächlich " + + "merge-blockierend erzwungen sein; ein grüner freiwilliger CI-Job, den " + + "GitHub beim Merge ignorieren könnte, erfüllt dieses Acceptance-Gate nicht."; /** Pflichtabschnitte — das Fehlen eines Abschnitts hebt eine Zusicherung auf. */ const REQUIRED_SECTIONS = [ @@ -77,6 +87,7 @@ const REQUIRED_SECTIONS = [ "Browser Integrity", "Autorisierung", "EYT-148 Final Journey Gate", + "Testintegrität", "PO Acceptance Evidence", "Human Gate", ] as const; @@ -188,6 +199,19 @@ describe("Sprint-7-UI/UX-Abnahmevertrag (EYT-148)", () => { expect(rule).toContain(MARKER_REAL_STACK); }); + it("erzwingt den Gegenbeweis-Grundsatz der Testintegritaet (EYT-148)", () => { + // Ohne diesen Satz koennte ein dauerhaft gruener Journey-Test als + // Abnahmenachweis gelten, ohne je bewiesen zu haben, dass er einen + // gebrochenen Auth-/Persistenz-/Routingpfad ueberhaupt erkennt. + expect(rule).toContain(MARKER_COUNTEREXAMPLE); + }); + + it("erzwingt merge-blockierende Sprint-7-Gates vor finaler Abnahme", () => { + // Ohne diesen Satz koennte ein freiwilliger (nicht required) CI-Job als + // EYT-148-Gate ausgegeben werden, den GitHub beim Merge ignoriert. + expect(rule).toContain(MARKER_MERGE_BLOCKING); + }); + it("wird von CLAUDE.md als bindend referenziert", () => { const claudeMd = readFileSync(join(repoRoot, "CLAUDE.md"), "utf8"); expect(claudeMd).toContain(RULE_PATH);