539 lines
27 KiB
HTML
539 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>KI als Team, nicht als Werkzeug</title>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/reveal.css" />
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/theme/black.css" />
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/plugin/highlight/monokai.css" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" />
|
||
<style>
|
||
/* ── Tokens (same as persona cards) ─────────────────── */
|
||
:root {
|
||
--bg: #0D1117;
|
||
--surface: #161B22;
|
||
--surface-2: #1C2128;
|
||
--border: #21262D;
|
||
--text: #C9D1D9;
|
||
--text-muted: #6E7681;
|
||
--text-bright:#F0F6FC;
|
||
--accent: #22D3EE;
|
||
--red: #F85149;
|
||
|
||
/* reveal overrides */
|
||
--r-background-color: var(--bg);
|
||
--r-main-color: var(--text);
|
||
--r-heading-color: var(--text-bright);
|
||
--r-link-color: var(--accent);
|
||
--r-link-color-hover: #67E8F9;
|
||
--r-main-font: 'Inter', system-ui, sans-serif;
|
||
--r-heading-font: 'Inter', system-ui, sans-serif;
|
||
--r-code-font: 'JetBrains Mono', monospace;
|
||
--r-main-font-size: 36px;
|
||
}
|
||
|
||
/* ── Dot-grid background (persona card pattern) ──────── */
|
||
.reveal::before {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
background-image: radial-gradient(circle, rgba(255,255,255,.045) 1px, transparent 1px);
|
||
background-size: 26px 26px;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
.reveal .slides { z-index: 1; }
|
||
|
||
/* ── Headings ─────────────────────────────────────────── */
|
||
.reveal h1, .reveal h2, .reveal h3 {
|
||
font-family: 'Inter', sans-serif;
|
||
font-weight: 800;
|
||
letter-spacing: -.5px;
|
||
text-transform: none;
|
||
color: var(--text-bright);
|
||
}
|
||
.reveal h2 {
|
||
font-size: 1.8em;
|
||
padding-bottom: .35em;
|
||
border-bottom: 1px solid var(--border);
|
||
margin-bottom: .6em;
|
||
}
|
||
.reveal h2::before {
|
||
content: '// ';
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: .7em;
|
||
color: var(--accent);
|
||
font-weight: 500;
|
||
}
|
||
.reveal h3 {
|
||
font-size: 1.15em;
|
||
color: var(--accent);
|
||
margin-bottom: .5em;
|
||
}
|
||
|
||
/* ── Body text ───────────────────────────────────────── */
|
||
.reveal p, .reveal li { color: var(--text); font-size: .85em; line-height: 1.6; }
|
||
.reveal strong { color: var(--text-bright); }
|
||
.reveal em { color: var(--text-muted); font-style: italic; }
|
||
.reveal small { color: var(--text-muted); font-size: .65em; }
|
||
|
||
/* ── Links ───────────────────────────────────────────── */
|
||
.reveal a {
|
||
color: var(--accent);
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: .75em;
|
||
text-decoration: none;
|
||
border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
|
||
}
|
||
.reveal a:hover { color: #67E8F9; border-bottom-color: #67E8F9; }
|
||
|
||
/* ── Tables ──────────────────────────────────────────── */
|
||
.reveal table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: .72em;
|
||
}
|
||
.reveal table thead tr {
|
||
border-bottom: 1px solid var(--accent);
|
||
}
|
||
.reveal table th {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: .85em;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.2px;
|
||
color: var(--text-muted);
|
||
padding: 6px 12px;
|
||
text-align: left;
|
||
}
|
||
.reveal table td {
|
||
padding: 7px 12px;
|
||
border-bottom: 1px solid var(--border);
|
||
color: var(--text);
|
||
vertical-align: top;
|
||
}
|
||
.reveal table tbody tr:last-child td { border-bottom: none; }
|
||
.reveal table tbody tr:hover td { background: rgba(255,255,255,.03); }
|
||
|
||
/* ── Code blocks ─────────────────────────────────────── */
|
||
.reveal pre {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-left: 3px solid var(--accent);
|
||
border-radius: 6px;
|
||
font-size: .58em;
|
||
box-shadow: none;
|
||
}
|
||
.reveal code:not(pre code) {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: .85em;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 1px 6px;
|
||
color: var(--accent);
|
||
}
|
||
|
||
/* ── Blockquotes ─────────────────────────────────────── */
|
||
.reveal blockquote {
|
||
background: color-mix(in srgb, var(--accent) 5%, transparent);
|
||
border-left: 3px solid var(--accent);
|
||
border-radius: 0 6px 6px 0;
|
||
padding: 12px 20px;
|
||
font-style: italic;
|
||
font-size: .8em;
|
||
color: var(--text-muted);
|
||
box-shadow: none;
|
||
width: 100%;
|
||
}
|
||
.reveal blockquote p { color: var(--text-muted); font-size: 1em; }
|
||
|
||
/* ── Topbar label (reusable) ─────────────────────────── */
|
||
.slide-label {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: .45em;
|
||
color: var(--text-muted);
|
||
letter-spacing: 1px;
|
||
display: block;
|
||
margin-bottom: .5em;
|
||
}
|
||
.slide-label .accent { color: var(--accent); }
|
||
|
||
/* ── Slide number ────────────────────────────────────── */
|
||
.reveal .slide-number {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
color: var(--text-muted);
|
||
padding: 2px 7px;
|
||
bottom: 12px;
|
||
right: 14px;
|
||
}
|
||
|
||
/* ── Progress bar ────────────────────────────────────── */
|
||
.reveal .progress { height: 2px; }
|
||
.reveal .progress span { background: var(--accent); }
|
||
|
||
/* ── highlight-row fragment: visible from start, styled on advance ── */
|
||
.reveal .fragment.highlight-row { opacity: 1; visibility: inherit; }
|
||
.reveal .fragment.highlight-row.visible {
|
||
font-weight: 700;
|
||
color: #22D3EE !important;
|
||
background: rgba(34,211,238,.08) !important;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="reveal">
|
||
<div class="slides">
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
1 · TITEL
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>KI als Team, nicht als Werkzeug</h2>
|
||
<p><small>Marcel Raddatz · 2026</small></p>
|
||
<aside class="notes">
|
||
Einstieg: „Die meisten nutzen KI als sehr schnelle Schreibkraft. Ich nutze sie als Team von Spezialisten."
|
||
Kein Agenda-Slide — das Workflow-Diagramm kommt nach der Team-Vorstellung.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
2 · DAS PROJEKT
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<section>
|
||
<h2>Das Projekt: Familienarchiv</h2>
|
||
<p>Handgeschriebene Kurrent- und Sütterlin-Briefe, 1899–1950</p>
|
||
<ul>
|
||
<li>Crowd-Transkription durch 60+-Jährige am Laptop</li>
|
||
<li>Jüngere Familienmitglieder lesen's am Handy</li>
|
||
<li>Solo-Entwickler — kein Team zum Review oder Pair-Programming</li>
|
||
</ul>
|
||
<aside class="notes">PM-Takeaway: echte Domäne, echte Nutzer, echte Constraints.</aside>
|
||
</section>
|
||
<section>
|
||
<h3>Stack & Umfang</h3>
|
||
<table>
|
||
<tr><td><strong>Backend</strong></td><td>Spring Boot 4 · Java 21 · JPA · Flyway · Spring Security</td></tr>
|
||
<tr><td><strong>Frontend</strong></td><td>SvelteKit 2 / Svelte 5 · TypeScript · Tailwind CSS 4</td></tr>
|
||
<tr><td><strong>DB / Storage</strong></td><td>PostgreSQL 16 · MinIO (S3-kompatibel)</td></tr>
|
||
<tr><td><strong>OCR</strong></td><td>Python · FastAPI · lernende ML-Modelle für Kurrent & Sütterlin</td></tr>
|
||
<tr><td><strong>i18n</strong></td><td>Paraglide.js — de / en / es</td></tr>
|
||
<tr><td><strong>Umfang</strong></td><td>40+ UI/UX-Specs · 360+ Gitea-Issues · 7 Personas</td></tr>
|
||
</table>
|
||
<aside class="notes">Dev-Takeaway: kein Toy-Projekt — voller Stack, Backend und Frontend.</aside>
|
||
</section>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
3 · DAS PROBLEM MIT "EINFACH FRAGEN"
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Das Problem mit „Einfach die KI fragen"</h2>
|
||
<table>
|
||
<thead><tr><th>Was man tut</th><th>Was schiefläuft</th></tr></thead>
|
||
<tbody>
|
||
<tr class="fragment"><td>Vage Idee → Code anfordern</td><td>Output passt nicht zu dem, was man wollte</td></tr>
|
||
<tr class="fragment"><td>Review in derselben Session</td><td>Befangenheit: genehmigt sich selbst</td></tr>
|
||
<tr class="fragment"><td>Die KI einfach fragen ob's gut ist</td><td>Kein echter Widerspruch</td></tr>
|
||
<tr class="fragment"><td>Auf Gesprächskontext vertrauen</td><td>Nächste Session startet blank</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="fragment"><em>Das ist ein Workflow-Problem, kein Prompt-Problem.</em></p>
|
||
<aside class="notes">
|
||
Dieser Slide schafft das Warum für alles was folgt.
|
||
Das Publikum erkennt sich in mindestens einer Zeile wieder.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
4 · STUFEN DER KI-ENTWICKLUNG
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Wo stehst du?</h2>
|
||
<table style="font-size:.6em;border-collapse:collapse;width:100%">
|
||
<tbody>
|
||
<tr style="color:#6E7681"><td style="padding:4px 10px;white-space:nowrap">1 — Autocomplete</td><td style="padding:4px 10px">Tab-Completion, eine Zeile vorschlagen</td></tr>
|
||
<tr style="color:#6E7681"><td style="padding:4px 10px;white-space:nowrap">2 — Chat / Pair</td><td style="padding:4px 10px">Fragen stellen, Code kopieren, selbst einfügen</td></tr>
|
||
<tr style="color:#6E7681"><td style="padding:4px 10px;white-space:nowrap">3 — Agentic Edit</td><td style="padding:4px 10px">KI editiert Dateien direkt — Cursor, Claude Code</td></tr>
|
||
<tr style="color:#6E7681"><td style="padding:4px 10px;white-space:nowrap">4 — Spec → Code</td><td style="padding:4px 10px">Mensch schreibt Spec, KI implementiert</td></tr>
|
||
<tr class="fragment highlight-row" style="color:#6E7681"><td style="padding:6px 10px;white-space:nowrap">5 — Idea → Spec → Code</td><td style="padding:6px 10px">KI hilft beim Spec-Schreiben — Mensch behält jedes Gate</td></tr>
|
||
<tr style="color:#6E7681"><td style="padding:4px 10px;white-space:nowrap">6 — Fully Autonomous</td><td style="padding:4px 10px">KI plant, spezifiziert, implementiert, testet — kein Mensch</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<aside class="notes">
|
||
Kurz im Publikum fragen: wer nutzt Autocomplete? Wer Chat? Wer Agentic?
|
||
Pointe: Stufe 6 klingt verlockend — aber ohne Gates verliert man Kontrolle und Kontext.
|
||
Stufe 5 ist der Sweet Spot: KI als Denkpartner, Mensch als Entscheider.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
5 · WORKFLOW ÜBERBLICK
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Der Workflow im Überblick</h2>
|
||
<pre style="font-size:.42em;overflow:visible"><code class="text" data-trim>
|
||
Vage Idee
|
||
│
|
||
▼
|
||
UI-Exploration ──▶ Spec (Elicit) ──▶ Gitea-Issue
|
||
│
|
||
▼
|
||
Persona-Review ──▶ discuss(Persona) ─┐
|
||
(6 Spezialisten) │
|
||
▲ │
|
||
└──────────────────────────────┘
|
||
│
|
||
▼
|
||
Feature-Branch
|
||
│
|
||
TDD: Red → Green → Refactor → Commit
|
||
│
|
||
▼
|
||
Pull Request ──▶ Persona-PR-Review ──▶ Merge
|
||
</code></pre>
|
||
<aside class="notes">
|
||
Roter Faden: wir verfolgen Issue #358 (Stammbaum) durch jeden Schritt.
|
||
Jeder folgende Abschnitt zoomt in einen Schritt hinein.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
6 · DAS TEAM
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Das Team</h2>
|
||
<table>
|
||
<thead><tr><th>Name</th><th>Rolle</th><th>Kernprinzip</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Elicit</td><td>Requirements Engineer</td><td>Schreibt nie Code</td></tr>
|
||
<tr><td>Markus Keller</td><td>Architekt</td><td>Boring technology wins</td></tr>
|
||
<tr><td>Felix Brandt</td><td>Senior Developer</td><td>Test first. Immer.</td></tr>
|
||
<tr><td>Leonie Voss</td><td>UX Design Lead</td><td>Hardest constraint first</td></tr>
|
||
<tr><td>Sara Holt</td><td>QA Engineer</td><td>A passing test that was never failing is a lie</td></tr>
|
||
<tr><td>Nora „NullX" Steiner</td><td>Security Engineer</td><td>Jeder neue Endpoint braucht <code>@RequirePermission</code></td></tr>
|
||
<tr><td>Tobias „tobi" Wendt</td><td>DevOps</td><td>Komplexität ist eine Liability</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<aside class="notes">
|
||
Markdown-Dateien in .claude/personas/ — jede mit echtem Namen und Biografie.
|
||
Überleitung: jetzt zoomen wir in jede Persona hinein.
|
||
</aside>
|
||
</section>
|
||
|
||
|
||
<section>
|
||
<span class="slide-label"><span class="accent">.claude/personas/</span>developer.md</span>
|
||
<h2>Aufbau einer Persona</h2>
|
||
<div style="display:grid;grid-template-columns:auto 1fr;gap:1.6em;margin-top:.5em;align-items:start">
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.48em;line-height:1.85;border-right:1px solid var(--border);padding-right:1.5em;white-space:nowrap">
|
||
<div style="color:var(--text-muted)">## Your Identity</div>
|
||
<div style="color:var(--text-muted)">## Readable & Clean Code</div>
|
||
<div style="color:var(--text-muted)">## Reliable Code</div>
|
||
<div style="color:var(--text-muted)">## Modern Code</div>
|
||
<div style="color:var(--accent);font-weight:700">## Secure Code ◀</div>
|
||
<div style="color:var(--text-muted)">## Testable Code</div>
|
||
<div style="color:var(--text-muted)">## How You Work</div>
|
||
<div style="color:var(--text-muted)">## Relationships</div>
|
||
<div style="color:var(--text-muted)">## Your Tone</div>
|
||
</div>
|
||
<div style="background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:0 6px 6px 0;padding:12px 18px;display:flex;flex-direction:column;gap:.55em">
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.48em;font-weight:700;color:var(--accent)">## Secure Code</div>
|
||
|
||
<div>
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.37em;color:var(--text-muted);letter-spacing:1px;text-transform:uppercase;margin-bottom:.3em">### General — projektunabhängig</div>
|
||
<p style="font-size:.58em;line-height:1.6;color:var(--text-muted);font-style:italic">
|
||
Secure code treats all external input as hostile. Authentication and authorization
|
||
are enforced via framework annotations, not scattered if-statements.
|
||
<strong style="color:var(--text-bright);font-style:normal">Error messages reveal nothing about the implementation.</strong>
|
||
</p>
|
||
</div>
|
||
|
||
<div>
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.37em;color:var(--text-muted);letter-spacing:1px;text-transform:uppercase;margin-bottom:.35em">### per Stack — projektspezifisch (Frontend · Java · Python)</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5em;font-size:.58em">
|
||
<div style="background:rgba(52,211,153,.07);border:1px solid rgba(52,211,153,.25);border-radius:5px;padding:7px 12px">
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.8em;color:var(--accent);font-weight:700;margin-bottom:.25em">✓ DO</div>
|
||
<div style="color:var(--text);line-height:1.45"><strong>Java:</strong> <code>@RequirePermission</code> on every write endpoint</div>
|
||
<div style="color:var(--text);line-height:1.45;margin-top:.4em"><strong>Python:</strong> SSRF host whitelist before any outbound request</div>
|
||
</div>
|
||
<div style="background:rgba(248,81,73,.07);border:1px solid rgba(248,81,73,.25);border-radius:5px;padding:7px 12px">
|
||
<div style="font-family:'JetBrains Mono',monospace;font-size:.8em;color:var(--red);font-weight:700;margin-bottom:.25em">✗ DON'T</div>
|
||
<div style="color:var(--text);line-height:1.45"><strong>Java:</strong> string concat in JPQL queries</div>
|
||
<div style="color:var(--text);line-height:1.45;margin-top:.4em"><strong>Svelte:</strong> <code>fetch('/api/…')</code> inside <code>onMount</code></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<aside class="notes">
|
||
Alle Personas teilen dieselbe Abschnittsstruktur — aber jede schreibt sie aus ihrer eigenen Perspektive.
|
||
Dieser Abschnitt ist Felix' Kernphilosophie: TDD ist kein Tool, es ist die einzige Arbeitsweise.
|
||
"Never write implementation code before a failing test exists" — das ist sein Hard Limit.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
7 · IDEE & UI-EXPLORATION
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Idee & UI-Exploration</h2>
|
||
<ol>
|
||
<li class="fragment">Vage Anforderung: <em>„Wir brauchen einen Stammbaum"</em></li>
|
||
<li class="fragment">UI-Persona bitten, <strong>4 Specs mit unterschiedlichem Fokus</strong> zu generieren</li>
|
||
<li class="fragment">Einen Spec auswählen — Elemente aus anderen übernehmen</li>
|
||
<li class="fragment">Finales vollständiges Spec schreiben lassen</li>
|
||
<li class="fragment">Committen und am Issue verlinken</li>
|
||
</ol>
|
||
<p class="fragment"><a href="../specs/stammbaum-tree-spec.html" target="_blank" style="font-family:monospace;font-size:.7em;color:#A6DAD8">→ stammbaum-tree-spec.html</a></p>
|
||
<aside class="notes">
|
||
stammbaum-tree-spec.html im Browser zeigen.
|
||
Der Trick mit den 4 Varianten: man merkt erst beim Vergleichen was man eigentlich will.
|
||
Scope-Entscheidungen auf Pixel-Ebene sind billiger als im Issue.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
7 · SPEC MIT ELICIT → GITEA-ISSUE
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<section>
|
||
<h2>Spec mit Elicit → Gitea-Issue</h2>
|
||
<p>Warum Gitea, nicht eine Datei?</p>
|
||
<ul>
|
||
<li class="fragment">LLM-Gespräche sind <strong>flüchtig</strong> — Issues sind <strong>dauerhaft</strong></li>
|
||
<li class="fragment">Issues sind verlinkbar: jeder Commit referenziert sie</li>
|
||
<li class="fragment">Review-Kommentare, Spec und Entscheidungen leben im selben Thread</li>
|
||
<li class="fragment">Kein Risiko einer veralteten Spec-Datei im Repo</li>
|
||
</ul>
|
||
<aside class="notes">
|
||
Das ist die architektonische Kernidee des ganzen Talks.
|
||
</aside>
|
||
</section>
|
||
<section>
|
||
<h3>Aufbau eines Spec-Issues</h3>
|
||
<ul>
|
||
<li class="fragment"><strong>Problem statement</strong> — was fehlt und warum es wichtig ist</li>
|
||
<li class="fragment"><strong>User Journey</strong> — Schritt für Schritt in Prosa</li>
|
||
<li class="fragment"><strong>E2E-Szenarien</strong> (Given/When/Then) → werden zu Red-Tests</li>
|
||
<li class="fragment"><strong>Requirements</strong> — funktional + NFR</li>
|
||
<li class="fragment"><strong>Out of scope</strong> — explizite Ausschlüsse</li>
|
||
</ul>
|
||
<p class="fragment"><a href="http://heim-nas:3005/marcel/familienarchiv/issues/358" target="_blank" style="font-family:monospace;font-size:.7em;color:#A6DAD8">→ Issue #358 — Stammbaum</a></p>
|
||
<aside class="notes">Echtes Issue #358 zeigen — annotiert.
|
||
PM: das ist der Vertrag vor Baubeginn.
|
||
Dev: das Szenario IST der erste fehlschlagende Test.</aside>
|
||
</section>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
8 · PERSONA ISSUE REVIEW
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Persona Issue Review</h2>
|
||
<p>Vor dem ersten Code: die Spec reviewen lassen</p>
|
||
<ul>
|
||
<li class="fragment">Jede Persona liest das Issue für sich — kein Austausch untereinander</li>
|
||
<li class="fragment">Kommentare werden direkt als Gitea-Kommentare gepostet</li>
|
||
<li class="fragment">Nora: Auth spezifiziert? · Sara: testbar? · Leonie: Mobile? · Markus: Domain-Grenzen?</li>
|
||
</ul>
|
||
<p class="fragment"><a href="http://heim-nas:3005/marcel/familienarchiv/issues/358#issuecomment-5176" target="_blank" style="font-family:monospace;font-size:.7em;color:#A6DAD8">→ Issue #358 — Kommentar</a></p>
|
||
<aside class="notes">review-issue-Skill schickt einen Agenten pro Persona.
|
||
„Hier zu finden kostet null. Nach dem PR-Öffnen kostet es einen Tag."
|
||
Den Kommentar zeigen der den größten Impact hatte.</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
9 · IMPLEMENTIERUNG: TDD
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<section>
|
||
<h2>Implementierung: Red/Green/Refactor</h2>
|
||
<ul>
|
||
<li class="fragment">Die Szenarien aus dem Issue werden direkt zu Unit-Tests</li>
|
||
<li class="fragment">Failing Unit-Test → minimaler Code → grün → Refactor → Commit</li>
|
||
<li class="fragment">Immer Feature-Branch · ein logischer Change pro Commit</li>
|
||
</ul>
|
||
<aside class="notes">deliver-issue-Skill koordiniert den gesamten Zyklus.
|
||
PM: die Akzeptanzkriterien aus dem Issue sind die Definition of Done.</aside>
|
||
</section>
|
||
<section>
|
||
<h3>Red/Green im git log</h3>
|
||
<pre><code class="java" data-trim>
|
||
// Erst der Test — schlägt fehl (RED)
|
||
@Test
|
||
void getFamilyNetwork_excludesEdgesWithNonFamilyEndpoints() {
|
||
var result = service.getFamilyNetwork(personId);
|
||
assertThat(result.edges())
|
||
.noneMatch(e -> isNonFamilyPerson(e.targetId()));
|
||
}
|
||
</code></pre>
|
||
<pre><code class="text" data-trim>
|
||
32622b9b test(stammbaum): getFamilyNetwork excludes edges ... ← RED
|
||
656c93ca fix(stammbaum): exclude non-family edges in network ← GREEN
|
||
</code></pre>
|
||
<aside class="notes">
|
||
Das Log ist der Beweis, dass der Red-Schritt stattgefunden hat.
|
||
Test und Implementierung im selben atomaren Commit.
|
||
</aside>
|
||
</section>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
10 · PR REVIEW LOOP
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>PR Review Loop</h2>
|
||
<p><code>review-pr</code>-Skill liest den Diff — jede Persona postet Kommentare zur PR</p>
|
||
<ul>
|
||
<li class="fragment">Felix: Projektstil, Dead Code?</li>
|
||
<li class="fragment">Nora: <code>@RequirePermission</code> auf jedem neuen Endpoint?</li>
|
||
<li class="fragment">Sara: fehlende Randfallstests?</li>
|
||
<li class="fragment">Leonie: stimmt der Output mit der Spec überein?</li>
|
||
</ul>
|
||
<p class="fragment">Kommentare adressieren → pushen → Review wiederholen → Merge</p>
|
||
<p class="fragment"><a href="http://heim-nas:3005/marcel/familienarchiv/pulls/360" target="_blank" style="font-family:monospace;font-size:.7em;color:#A6DAD8">→ PR #360 — feat(stammbaum)</a></p>
|
||
<aside class="notes">
|
||
Einen PR-Kommentar zeigen der den Merge blockiert hat.
|
||
Merge erst wenn alle Stimmen zufrieden sind.
|
||
</aside>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
11 · FAZIT
|
||
══════════════════════════════════════════════ -->
|
||
<section>
|
||
<h2>Fazit</h2>
|
||
<ol>
|
||
<li><strong>Separation of Concerns gilt auch für KI-Rollen</strong> — eine Persona die alles macht, reviewed sich selbst</li>
|
||
<li><strong>Das LLM vergisst. Das Issue nicht.</strong> — Spec, Entscheidungen und Review-Kommentare überleben jeden Context Reset</li>
|
||
</ol>
|
||
<aside class="notes">
|
||
Ehrliches Caveat: Aufwand am Anfang. Der Ertrag ist kumulativ.
|
||
</aside>
|
||
</section>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/reveal.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5/plugin/highlight/highlight.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5/plugin/notes/notes.js"></script>
|
||
<script>
|
||
Reveal.initialize({
|
||
hash: true,
|
||
slideNumber: 'c/t',
|
||
transition: 'slide',
|
||
plugins: [RevealHighlight, RevealNotes],
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|