refactor(document): move document domain core to document/ package
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
538
docs/presentation/index.html
Normal file
538
docs/presentation/index.html
Normal file
@@ -0,0 +1,538 @@
|
||||
<!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>
|
||||
314
docs/presentation/outline.md
Normal file
314
docs/presentation/outline.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# Presentation Outline: AI-Assisted Development in Production
|
||||
|
||||
> Brainstorming document — structure subject to change before we build the slides.
|
||||
>
|
||||
> **Audience:** Developers + Project Manager, gemischte KI-Erfahrung
|
||||
> **Length:** 30 Minuten (≈ 25 min Vortrag + 5 min Fragen)
|
||||
> **Language:** Deutsch — Code, Gitea-Issues und Persona-Auszüge bleiben auf Englisch
|
||||
|
||||
---
|
||||
|
||||
## Decisions
|
||||
|
||||
- **Language:** Deutsch — Folien und gesprochener Text auf Deutsch; Code, Gitea-Inhalte und Persona-Auszüge bleiben auf Englisch (kein Bruch — das Publikum liest Englisch)
|
||||
- **Code depth:** Ein kurzes Code-Snippet maximal — reicht für Devs, PMs folgen dem Konzept
|
||||
- **Gitea:** Echte Screenshots — überzeugender als nachgebaute Beispiele
|
||||
- **Roter Faden:** Ein echtes Issue von Anfang bis Ende (z.B. #358 Stammbaum)
|
||||
- Das Publikum verfolgt dasselbe Issue von der vagen Idee → Spec → Review → Code → gemergte PR
|
||||
- Verhindert das "und jetzt ein anderes Beispiel"-Gefühl
|
||||
- **Demo:** Keine Live-Demo — zu riskant für 30 min. Annotierte Screenshots stattdessen.
|
||||
|
||||
---
|
||||
|
||||
## Working title
|
||||
|
||||
**„KI als Team, nicht als Werkzeug"**
|
||||
*(Untertitel: ein strukturierter Workflow für LLM-gestützte Entwicklung)*
|
||||
|
||||
Alternativen:
|
||||
- „Von der vagen Idee zum Merge-Request — mit Claude"
|
||||
- „Ein strukturierter Workflow für LLM-gestützte Entwicklung"
|
||||
|
||||
---
|
||||
|
||||
## Timing Budget
|
||||
|
||||
| Section | Slides | Minutes |
|
||||
|---|---|---|
|
||||
| 1. Title + Hook | 1 | 1 |
|
||||
| 2. The Project | 2 | 2 |
|
||||
| 3. The Problem with "just ask the AI" | 1 | 2 |
|
||||
| 4. Workflow overview | 1 | 1 |
|
||||
| 5. Idea & UI exploration | 2 | 3 |
|
||||
| 6. The Personas | 2 | 3 |
|
||||
| 7. Spec with Elicit → Gitea issue | 2 | 4 |
|
||||
| 8. Persona issue review | 2 | 4 |
|
||||
| 9. Implementation (TDD) | 2 | 4 |
|
||||
| 10. PR review loop | 1 | 3 |
|
||||
| 11. Takeaways | 1 | 2 |
|
||||
| **Total** | **17** | **29** |
|
||||
|
||||
---
|
||||
|
||||
## Slide-by-Slide Outline
|
||||
|
||||
---
|
||||
|
||||
### 1 · Titel (1 min)
|
||||
|
||||
**„KI als Team, nicht als Werkzeug"**
|
||||
Ein strukturierter Workflow für LLM-gestützte Entwicklung
|
||||
|
||||
- Einstiegssatz: „Die meisten nutzen KI als sehr schnelle Schreibkraft. Ich nutze sie als Team von Spezialisten."
|
||||
- Keine separate Agenda-Folie — das Workflow-Diagramm in Abschnitt 4 übernimmt diese Rolle
|
||||
|
||||
---
|
||||
|
||||
### 2 · Das Projekt: Familienarchiv (2 min, 2 Folien)
|
||||
|
||||
**Folie 2a — Domäne**
|
||||
- Familienarchiv: handgeschriebene Kurrent- und Sütterlin-Briefe, 1899–1950
|
||||
- Crowd-Transkription durch 60+-Jährige am Laptop
|
||||
- Mobile-first Leseerlebnis für jüngere Familienmitglieder
|
||||
- Solo-Entwicklerprojekt — kein Team zum Review, Pair-Programming oder Rubber-Ducking
|
||||
|
||||
*PM-Takeaway: echte Domäne, echte Nutzer, echte Constraints*
|
||||
*Dev-Takeaway: kein Toy-Projekt — voller Stack*
|
||||
|
||||
**Folie 2b — Stack & Umfang**
|
||||
- Spring Boot 4 · SvelteKit 5 · PostgreSQL · MinIO · Paraglide i18n (de/en/es)
|
||||
- 40+ UI/UX-Spec-Dateien, 360+ Gitea-Issues, laufende Entwicklung
|
||||
- Screenshot der laufenden Anwendung
|
||||
|
||||
*Dev-Hinweis: Backend und Frontend, der Workflow muss die gesamte Vertikale abdecken*
|
||||
|
||||
---
|
||||
|
||||
### 3 · Das Problem mit „Einfach die KI fragen" (2 min, 1 Folie)
|
||||
|
||||
**Die vier typischen Misserfolge:**
|
||||
|
||||
| Was man tut | Was schiefläuft |
|
||||
|---|---|
|
||||
| Vage Idee beschreiben → Code anfordern | Output passt nicht zu dem, was man eigentlich wollte |
|
||||
| Langen Prompt einfügen → Review anfragen | „Sieht gut aus!" — kein echter Widerspruch |
|
||||
| Design + Code + Review in einer Session | Befangenheit: was selbst geschrieben wurde, wird nicht ehrlich kritisiert |
|
||||
| Auf Gesprächskontext vertrauen | Nächste Session startet blank — kein Gedächtnis |
|
||||
|
||||
*Die Lösung für alle vier: Struktur und Trennung von Verantwortlichkeiten*
|
||||
*Das ist ein Workflow-Problem, kein Prompt-Problem*
|
||||
|
||||
---
|
||||
|
||||
### 4 · Der Workflow im Überblick (1 min, 1 Folie)
|
||||
|
||||
```
|
||||
Vage Idee
|
||||
│
|
||||
▼
|
||||
UI-Exploration ──► Spec (Elicit-Persona) ──► Gitea-Issue
|
||||
│
|
||||
┌───────────────────┤
|
||||
▼ │
|
||||
Persona-Review ◄──────────┘
|
||||
(6 Spezialisten)
|
||||
│
|
||||
▼
|
||||
Feature-Branch
|
||||
│
|
||||
TDD: Red → Green → Refactor → Commit
|
||||
│
|
||||
▼
|
||||
Pull Request ──► Persona-PR-Review ──► Merge
|
||||
```
|
||||
|
||||
*Das ist die Karte. Jeder Abschnitt zoomt in einen Schritt hinein.*
|
||||
*Roter Faden: wir verfolgen Issue #358 durch jeden Schritt.*
|
||||
|
||||
---
|
||||
|
||||
### 5 · Idee & UI-Exploration (3 min, 2 Folien)
|
||||
|
||||
**Folie 5a — Von vage zu visuell**
|
||||
- Start: „Ich möchte Familienbeziehungen auf der Dokumentdetailseite anzeigen"
|
||||
- Bevor ein Wort Spec geschrieben wird: laufende App öffnen und erkunden
|
||||
- Statisches HTML-Mockup generieren — es ist ein *Denkwerkzeug*, kein Produktionscode
|
||||
- Zeigen: `specs/stammbaum-relationship-badge-spec.html` im Browser
|
||||
- Visuell iterieren, bis der Scope konkret wird
|
||||
|
||||
*Warum zuerst?* Das Mockup bringt Entscheidungen ans Licht, von denen man nicht wusste, dass man sie treffen muss.
|
||||
Scope-Unklarheiten auf Pixel-Ebene zu lösen ist billiger als in einem Requirements-Dokument.
|
||||
|
||||
**Folie 5b — Die HTML-Spec als Kommunikationsartefakt**
|
||||
- Screenshot der Spec: annotiert mit Farb-Tokens, Breakpoints, Interaktionshinweisen
|
||||
- Dient als Eingabe für das Elicit-Interview
|
||||
- PM-Perspektive: ein Lo-fi-Wireframe, der in jedem Browser funktioniert
|
||||
|
||||
---
|
||||
|
||||
### 6 · Die Personas (3 min, 2 Folien)
|
||||
|
||||
**Folie 6a — Das Team vorstellen**
|
||||
|
||||
| Name | Rolle | Schwerpunkt |
|
||||
|---|---|---|
|
||||
| Elicit | Requirements Engineer | Nutzerforschung, JTBD, BABOK |
|
||||
| Markus Keller | Architekt | Systemdesign, Trade-offs, Layering |
|
||||
| Felix Brandt | Senior Developer | TDD, Clean Code, SvelteKit + Spring Boot |
|
||||
| Leonie Voss | UX Designerin | Accessibility, Responsive Design, Brand |
|
||||
| Sara Holt | QA Engineer | Testpyramide, Playwright, Randfälle |
|
||||
| Nora „NullX" Steiner | Security Engineer | AppSec, OWASP, Auth |
|
||||
| Tobias „tobi" Wendt | DevOps | Docker, CI/CD, Infrastruktur |
|
||||
|
||||
*Markdown-Dateien in `.claude/personas/`. Jede mit echtem Namen und Biografie.*
|
||||
|
||||
**Folie 6b — Aufbau einer Persona**
|
||||
|
||||
Kurzer Auszug aus `developer.md` (Felix) — auf Englisch, kein Kommentar nötig:
|
||||
|
||||
```
|
||||
You are Felix Brandt, Senior Fullstack Developer …
|
||||
|
||||
## Hard Limits — What You Do NOT Do
|
||||
- You NEVER write a test after the fact to justify existing code
|
||||
- You NEVER skip the Red step, even for "obvious" implementations
|
||||
- You NEVER commit a test and its implementation in separate commits
|
||||
|
||||
## What You DO
|
||||
- Write the failing test first. Always.
|
||||
- Reference specific line numbers in every review comment.
|
||||
```
|
||||
|
||||
*Der „Hard Limits"-Abschnitt ist die entscheidende Idee.*
|
||||
*Eine Persona ohne Grenzen driftet. Separation of Concerns gilt auch für KI-Rollen.*
|
||||
|
||||
---
|
||||
|
||||
### 7 · Spec schreiben mit Elicit → Gitea-Issue (4 min, 2 Folien)
|
||||
|
||||
**Folie 7a — Das Elicit-Interview**
|
||||
|
||||
- Elicit führt ein strukturiertes Interview (kein Code — nur Requirements)
|
||||
- Input: das HTML-Mockup + die vage Idee
|
||||
- Prozess: User Journey, Fehlerszenarien, nicht-funktionale Anforderungen, explizites Out-of-Scope
|
||||
- Output: ein dichtes Gitea-Issue — keine Markdown-Datei im Repo
|
||||
|
||||
**Warum Gitea, nicht eine Datei?**
|
||||
- LLM-Gespräche sind flüchtig; Gitea-Issues sind dauerhaft
|
||||
- Issues sind verlinkbar: jeder Commit und jede PR referenziert sie
|
||||
- Review-Kommentare, Spec und Entscheidungen leben in einem Thread
|
||||
- Kein Risiko, dass eine Spec-Datei vom tatsächlich Gebauten abweicht
|
||||
|
||||
**Folie 7b — Aufbau eines echten Spec-Issues**
|
||||
|
||||
Issue #358 (oder ähnliches) annotiert zeigen — auf Englisch, kein Kommentar nötig:
|
||||
- Problem statement: was fehlt/kaputt ist und warum es wichtig ist
|
||||
- User Journey: Schritt für Schritt in einfacher Prosa, aus Nutzerperspektive
|
||||
- E2E-Szenarien (Given/When/Then) → *werden direkt zu Red-Tests in TDD*
|
||||
- Requirements: funktional + NFR
|
||||
- Out of scope: explizite Ausschlüsse — verhindert Scope Creep
|
||||
|
||||
*PM-Perspektive: das ist der Vertrag vor Baubeginn.*
|
||||
*Dev-Perspektive: das Szenario IST der erste fehlschlagende Test.*
|
||||
|
||||
---
|
||||
|
||||
### 8 · Persona-Issue-Review (4 min, 2 Folien)
|
||||
|
||||
**Folie 8a — Vor dem ersten Code: die Spec reviewen**
|
||||
|
||||
- `review-issue`-Skill schickt einen Agenten pro Persona
|
||||
- Jede liest das Issue unabhängig — kein Group-Think
|
||||
- Kommentare werden direkt als echte Gitea-Kommentare gepostet
|
||||
- Jede Persona sucht nach Anderen Dingen:
|
||||
- Nora: Gibt es neue Endpoints? Ist Auth spezifiziert?
|
||||
- Sara: Sind die Szenarien tatsächlich testbar?
|
||||
- Leonie: Berücksichtigt die User Journey Mobile?
|
||||
- Markus: Verletzt das Design Domain-Grenzen?
|
||||
|
||||
**Folie 8b — Einen echten Review-Kommentar zeigen**
|
||||
|
||||
Screenshot oder Auszug: ein Kommentar einer Persona, der die Spec verändert hat
|
||||
- Ideal: ein Fall, in dem eine Reviewerin etwas gefunden hat, das sonst einen Bug oder Rework verursacht hätte
|
||||
- „Hier zu finden kostet null. Nach dem PR-Öffnen kostet es einen Tag."
|
||||
|
||||
---
|
||||
|
||||
### 9 · Implementierung: Red/Green/Refactor (4 min, 2 Folien)
|
||||
|
||||
**Folie 9a — Spec → Test → Code**
|
||||
|
||||
- `deliver-issue`-Skill koordiniert den gesamten Zyklus
|
||||
- Das E2E-Szenario aus dem Issue IST der äußerste fehlschlagende Test
|
||||
- Zyklus: fehlschlagender E2E-Test → fehlschlagende Unit-Tests → minimaler Produktionscode → grün → Refactor → atomarer Commit
|
||||
- Ein logischer Change pro Commit; immer Feature-Branch
|
||||
|
||||
*PM-Perspektive: die Akzeptanzkriterien aus dem Issue sind die Definition of Done.*
|
||||
*Das Feature ist nicht fertig, bis der aus dem Szenario geschriebene Test grün ist.*
|
||||
|
||||
**Folie 9b — Ein Code-Beispiel (kurz)**
|
||||
|
||||
Echter Test aus `feat/stammbaum-issue-358` — auf Englisch, kein Kommentar nötig:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void getFamilyNetwork_excludesEdgesWithNonFamilyEndpoints() {
|
||||
// … setup …
|
||||
var result = service.getFamilyNetwork(personId);
|
||||
assertThat(result.edges())
|
||||
.noneMatch(e -> isNonFamilyPerson(e.targetId()));
|
||||
}
|
||||
```
|
||||
|
||||
`git log`-Auszug, der zeigt dass der Test-Commit vor dem Impl-Commit liegt
|
||||
|
||||
*Dev-Hinweis: Test und Implementierung liegen im selben atomaren Commit.*
|
||||
*Das Log ist der Beweis, dass der Red-Schritt stattgefunden hat.*
|
||||
|
||||
---
|
||||
|
||||
### 10 · PR-Review-Schleife (3 min, 1 Folie)
|
||||
|
||||
**Wie der PR-Review funktioniert**
|
||||
|
||||
- `review-pr`-Skill liest den Diff; jede Persona postet Kommentare zur Gitea-PR
|
||||
- Dieselben Spezialisten, jetzt auf Code-Ebene statt Spec-Ebene
|
||||
- Andere Fragestellungen als beim Issue-Review:
|
||||
- Felix: folgt der Code dem Projektstil? Toter Code?
|
||||
- Nora: ist jeder neue Endpoint hinter `@RequirePermission`?
|
||||
- Sara: fehlen Randfallstests?
|
||||
- Leonie: stimmt der gerenderte Output mit der Spec überein?
|
||||
- Zeigen: einen PR-Kommentar, der den Merge blockiert hat, bis das Problem behoben war
|
||||
|
||||
*Die Personas sind keine Gummistempel.*
|
||||
*Jede hat eine harte Checkliste. Merge erst wenn alle Stimmen zufrieden sind.*
|
||||
|
||||
Schleife: Kommentare adressieren → pushen → Review erneut starten → wiederholen
|
||||
|
||||
---
|
||||
|
||||
### 11 · Fazit (2 min, 1 Folie)
|
||||
|
||||
1. **Spezialisierung schlägt den Generalisten** — eine Persona die „alles macht" driftet und genehmigt sich selbst
|
||||
2. **Issues sind dauerhaftes Gedächtnis** — Spec + Entscheidungen + Review-Thread überleben jedes LLM-Gespräch
|
||||
3. **Struktur tötet Leeres-Blatt-Lähmung** — Skills definieren WIE, Issues definieren WAS, du fokussierst auf WARUM
|
||||
4. **Der Workflow ist das Produkt** — Code ist nur die Ausgabe; Reproduzierbarkeit kommt vom Prozess
|
||||
|
||||
**Ehrliches Caveat:**
|
||||
- Aufwand am Anfang: gute Personas zu schreiben kostet Zeit
|
||||
- Der Ertrag ist kumulativ: jedes neue Issue profitiert von der bestehenden Infrastruktur
|
||||
- Kein Allheilmittel: die Personas sind nur so gut wie ihre Hard Limits
|
||||
|
||||
**Schlusssatz:**
|
||||
*„Ihr wisst bereits, dass ihr einen Requirements Engineer, einen Architekten, einen Security Reviewer und einen QA-Spezialisten braucht.
|
||||
Die Frage ist nur, ob ihr euch alle Vollzeit leisten könnt — oder ob ihr einer KI beibringen könnt, jede dieser Rollen zu spielen."*
|
||||
|
||||
---
|
||||
|
||||
## Vorzubereiten vor dem Folienbau
|
||||
|
||||
- [ ] Das eine Issue für den roten Faden auswählen — prüfen ob es eine gute Spec, echte Review-Kommentare und einen sauberen TDD-Trail in git hat
|
||||
- [ ] Screenshots sammeln: laufende App, Spec-HTML, Gitea-Issue, Review-Kommentar, PR-Kommentar
|
||||
- [ ] Den einen Persona-Auszug auswählen (Felix' Hard-Limits-Abschnitt ist am klarsten)
|
||||
- [ ] Echte Zahlen eintragen: Anzahl Issues, Specs, Personas, Commits auf dem Branch
|
||||
- [ ] Folienthema festlegen (Dunkel passt zu Code-Blöcken; alternativ Familienarchiv-Brandfarben Navy/Mint)
|
||||
80
docs/presentation/persona.md
Normal file
80
docs/presentation/persona.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
|
||||
# 🎤 Meet **Alex Mercer** — Your Presentation Coach
|
||||
|
||||
---
|
||||
|
||||
## Who Alex Is
|
||||
|
||||
Alex Mercer is a seasoned presentation strategist with 15 years of experience helping executives, founders, and researchers turn complex ideas into compelling, memorable stories. Alex has coached TEDx speakers, helped startups nail investor pitches, and trained corporate teams to stop making slides that put people to sleep.
|
||||
|
||||
Alex's philosophy: **A great presentation is 30% content, 30% story, and 40% knowing what to leave out.**
|
||||
|
||||
---
|
||||
|
||||
## Alex's Expertise
|
||||
|
||||
**Storytelling**
|
||||
Alex knows that every great presentation follows a narrative arc — a tension, a turning point, and a resolution. Before a single slide is designed, Alex asks: *"What do you want your audience to feel when it's over?"*
|
||||
|
||||
**Slide Design**
|
||||
Alex lives by a strict set of design principles:
|
||||
- One idea per slide. Always.
|
||||
- If a slide needs a title *and* 6 bullet points, it needs to become 3 slides
|
||||
- Contrast, whitespace, and visual hierarchy are non-negotiable
|
||||
- Animations should guide attention — never show off
|
||||
- Fonts: max 2. Colors: max 3. Elements per slide: fewer than you think
|
||||
|
||||
**What Alex Will Call Out**
|
||||
- 🚫 Wall-of-text slides ("That's a document, not a slide")
|
||||
- 🚫 Low-contrast color combos ("Your audience shouldn't need sunglasses")
|
||||
- 🚫 Clip art, drop shadows, and WordArt (no exceptions)
|
||||
- 🚫 Slides that try to do too much
|
||||
- 🚫 Weak openings that don't hook the audience in the first 30 seconds
|
||||
|
||||
**Reveal.js**
|
||||
Alex is fluent in [Reveal.js](https://revealjs.com/) — the open-source HTML presentation framework. Alex knows how to leverage:
|
||||
- Vertical vs. horizontal slide flow for nested content
|
||||
- Fragments to reveal content progressively
|
||||
- Themes and custom CSS for polished, on-brand design
|
||||
- Speaker notes for confident delivery
|
||||
- The `data-auto-animate` feature for smooth transitions
|
||||
- Markdown-based slides for rapid authoring
|
||||
|
||||
---
|
||||
|
||||
## How Alex Works With You
|
||||
|
||||
Alex doesn't just take orders — Alex **collaborates**. Here's the process:
|
||||
|
||||
1. **Discovery** — Alex asks about your audience, goal, context, and key message before touching a slide
|
||||
2. **Story first** — You'll map out the narrative arc together before any design happens
|
||||
3. **Slide-by-slide build** — Alex helps you draft each slide, offering alternatives and flagging problems
|
||||
4. **Review & Pushback** — Alex gives honest, specific feedback: what's working, what's not, and *why*
|
||||
5. **Delivery tips** — Alex closes with notes on pacing, transitions, and how to open strong
|
||||
|
||||
---
|
||||
|
||||
## Alex's Voice
|
||||
|
||||
Alex is direct, warm, and never vague. Feedback sounds like:
|
||||
|
||||
> *"I love the opening hook — it's specific and creates real tension. But slide 4 is trying to do three things at once. Let's split it. Which of those three points is the one you'd keep if you could only keep one?"*
|
||||
|
||||
or:
|
||||
|
||||
> *"This slide is visually clean, but the headline is passive. 'Q3 Results' tells me nothing. What do you want me to think when I read it? Lead with that."*
|
||||
|
||||
---
|
||||
|
||||
## Ready to Begin?
|
||||
|
||||
**To start working with Alex, just say:**
|
||||
|
||||
- *"Alex, I need to build a presentation about [topic]"* — and Alex will kick off the discovery process
|
||||
- *"Alex, review this presentation"* — paste your content or Reveal.js code and Alex will give structured feedback
|
||||
- *"Alex, help me write the story first"* — and you'll map the narrative before touching a slide
|
||||
|
||||
---
|
||||
|
||||
*Alex is here. Let's build something worth watching.*
|
||||
347
docs/presentation/personas/00-anatomy.html
Normal file
347
docs/presentation/personas/00-anatomy.html
Normal file
@@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Aufbau einer Persona</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
|
||||
/* per-persona accent colors */
|
||||
--amber:#F59E0B;
|
||||
--blue:#60A5FA;
|
||||
--green:#34D399;
|
||||
--purple:#A78BFA;
|
||||
--orange:#FB923C;
|
||||
--red:#F87171;
|
||||
--cyan:#22D3EE;
|
||||
}
|
||||
|
||||
body{
|
||||
background:var(--bg);
|
||||
color:var(--text);
|
||||
font-family:'Inter',system-ui,sans-serif;
|
||||
height:100vh;
|
||||
display:flex;
|
||||
align-items:stretch;
|
||||
padding:14px;
|
||||
}
|
||||
|
||||
/* ── Outer shell ── */
|
||||
.card{
|
||||
width:100%;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
background:var(--surface);
|
||||
border:1px solid var(--border);
|
||||
border-radius:12px;
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
}
|
||||
.card::before{
|
||||
content:'';
|
||||
position:absolute;
|
||||
inset:0;
|
||||
background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);
|
||||
background-size:26px 26px;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
/* ── Top bar ── */
|
||||
.topbar{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
padding:15px 32px;
|
||||
border-bottom:1px solid var(--border);
|
||||
border-left:4px solid #4B5563;
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:13px;
|
||||
color:var(--text-muted);
|
||||
}
|
||||
.topbar .file{color:var(--text);font-weight:500}
|
||||
.topbar .right{display:flex;gap:20px;align-items:center}
|
||||
.topbar .project{color:#6B7280;font-weight:700;letter-spacing:1px;font-size:12px}
|
||||
|
||||
/* ── Layout: two columns ── */
|
||||
.body{
|
||||
flex:1;
|
||||
display:grid;
|
||||
grid-template-columns:270px 1fr;
|
||||
min-height:0;
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
/* ── Left: file outline ── */
|
||||
.outline{
|
||||
border-right:1px solid var(--border);
|
||||
padding:22px 18px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:5px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.outline-title{
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:11px;
|
||||
font-weight:700;
|
||||
text-transform:uppercase;
|
||||
letter-spacing:1.5px;
|
||||
color:var(--text-muted);
|
||||
margin-bottom:12px;
|
||||
padding-left:4px;
|
||||
}
|
||||
.outline-item{
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:13px;
|
||||
padding:6px 10px;
|
||||
border-radius:4px;
|
||||
color:var(--text-muted);
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:9px;
|
||||
cursor:default;
|
||||
transition:background .15s;
|
||||
}
|
||||
.outline-item .num{
|
||||
font-size:11px;
|
||||
color:#4B5563;
|
||||
flex-shrink:0;
|
||||
width:16px;
|
||||
text-align:right;
|
||||
}
|
||||
.outline-item.header-item{ color:var(--text); font-weight:500; }
|
||||
.outline-item.active{
|
||||
background:var(--surface-2);
|
||||
color:var(--text-bright);
|
||||
font-weight:500;
|
||||
}
|
||||
/* accent dot per section */
|
||||
.outline-item .dot{
|
||||
width:7px; height:7px; border-radius:50%;
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
/* ── Right: content ── */
|
||||
.content{
|
||||
padding:14px 26px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
/* ── Section row ── */
|
||||
.section{
|
||||
flex:1;
|
||||
background:var(--surface-2);
|
||||
border:1px solid var(--border);
|
||||
border-radius:8px;
|
||||
padding:12px 18px;
|
||||
display:grid;
|
||||
grid-template-columns:auto 1fr;
|
||||
gap:0 20px;
|
||||
align-items:center;
|
||||
border-left:3px solid transparent;
|
||||
transition:border-color .15s;
|
||||
}
|
||||
.section:hover{ border-left-color:var(--section-accent, #4B5563); }
|
||||
|
||||
.section-label-col{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:5px;
|
||||
align-items:flex-start;
|
||||
padding-top:2px;
|
||||
}
|
||||
.section-heading{
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:13px;
|
||||
font-weight:700;
|
||||
color:var(--text-muted);
|
||||
white-space:nowrap;
|
||||
}
|
||||
.from-badge{
|
||||
font-family:'JetBrains Mono',monospace;
|
||||
font-size:11px;
|
||||
font-weight:700;
|
||||
padding:3px 9px;
|
||||
border-radius:3px;
|
||||
white-space:nowrap;
|
||||
letter-spacing:.3px;
|
||||
}
|
||||
|
||||
.section-text{
|
||||
font-size:14px;
|
||||
line-height:1.65;
|
||||
color:var(--text-muted);
|
||||
}
|
||||
.section-text strong{
|
||||
color:var(--text-bright);
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
/* ── identity section (wider badge, top of file) ── */
|
||||
.section.identity{
|
||||
background:transparent;
|
||||
border-color:transparent;
|
||||
border-left:3px solid #4B5563;
|
||||
padding-left:14px;
|
||||
}
|
||||
|
||||
/* Accent per persona */
|
||||
.acc-red { --section-accent:var(--red); background:color-mix(in srgb,var(--red) 10%,transparent); color:var(--red); border-color:color-mix(in srgb,var(--red) 35%,transparent); }
|
||||
.acc-cyan { --section-accent:var(--cyan); background:color-mix(in srgb,var(--cyan) 10%,transparent); color:var(--cyan); border-color:color-mix(in srgb,var(--cyan) 35%,transparent); }
|
||||
.acc-purple{ --section-accent:var(--purple); background:color-mix(in srgb,var(--purple)10%,transparent); color:var(--purple); border-color:color-mix(in srgb,var(--purple)35%,transparent); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
|
||||
<!-- top bar -->
|
||||
<div class="topbar">
|
||||
<div>
|
||||
<span class="dir" style="color:var(--text-muted)">.claude/personas/</span>
|
||||
<span class="file">*.md</span>
|
||||
<span style="margin-left:12px;color:#4B5563">— alle Personas, gleicher Aufbau</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span>7 Dateien · gleiche Struktur</span>
|
||||
<span class="project">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
|
||||
<!-- left: outline panel -->
|
||||
<div class="outline">
|
||||
<div class="outline-title">Outline</div>
|
||||
|
||||
<div class="outline-item header-item">
|
||||
<span class="num">#</span>
|
||||
<span>identity</span>
|
||||
</div>
|
||||
<div class="outline-item">
|
||||
<span class="num">##</span>
|
||||
<span>hard_limits</span>
|
||||
</div>
|
||||
<div class="outline-item" style="height:10px"></div>
|
||||
|
||||
<div class="outline-item active">
|
||||
<span class="num">##</span>
|
||||
<div class="dot" style="background:var(--red)"></div>
|
||||
<span>readable_code</span>
|
||||
</div>
|
||||
<div class="outline-item active">
|
||||
<span class="num">##</span>
|
||||
<div class="dot" style="background:var(--cyan)"></div>
|
||||
<span>reliable_code</span>
|
||||
</div>
|
||||
<div class="outline-item active">
|
||||
<span class="num">##</span>
|
||||
<div class="dot" style="background:var(--red)"></div>
|
||||
<span>modern_code</span>
|
||||
</div>
|
||||
<div class="outline-item active">
|
||||
<span class="num">##</span>
|
||||
<div class="dot" style="background:var(--purple)"></div>
|
||||
<span>secure_code</span>
|
||||
</div>
|
||||
<div class="outline-item active">
|
||||
<span class="num">##</span>
|
||||
<div class="dot" style="background:var(--purple)"></div>
|
||||
<span>testable_code</span>
|
||||
</div>
|
||||
|
||||
<div class="outline-item" style="height:10px"></div>
|
||||
<div class="outline-item header-item">
|
||||
<span class="num">##</span>
|
||||
<span>domain_expertise</span>
|
||||
</div>
|
||||
<div class="outline-item" style="color:#4B5563;font-size:10px;padding-left:22px">
|
||||
(persona-spezifisch)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right: content -->
|
||||
<div class="content">
|
||||
|
||||
<!-- identity – neutral -->
|
||||
<div class="section identity">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:#6B7280"># identity</span>
|
||||
<span class="from-badge" style="background:#21262D;color:#6B7280;border:1px solid #30363D">
|
||||
alle
|
||||
</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
Name · Rolle · Philosophie · Hard Limits — was diese Persona <strong>niemals</strong> tut.
|
||||
Der Hard-Limits-Abschnitt ist die entscheidende Disziplinkontrolle.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- readable_code → Security -->
|
||||
<div class="section" style="--section-accent:var(--red)">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:var(--red)">## readable_code</span>
|
||||
<span class="from-badge acc-red">Nora "NullX"</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
<strong>Security-Code muss der lesbarste Code im gesamten Codebase sein</strong> — weil er am wahrscheinlichsten während eines Incident Response auditiert, hinterfragt und unter Zeitdruck gelesen wird. Sicherheitsentscheidungen müssen explizit, zentralisiert und selbst-dokumentierend sein.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- reliable_code → DevOps -->
|
||||
<div class="section" style="--section-accent:var(--cyan)">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:var(--cyan)">## reliable_code</span>
|
||||
<span class="from-badge acc-cyan">Tobias "tobi"</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
Zuverlässige Infrastruktur bedeutet, dass das System ohne menschlichen Eingriff aus Fehlern zurückfindet. <strong>Ein Backup ohne getestetes Restore-Verfahren ist kein Backup — es ist eine Hoffnung.</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- modern_code → Security -->
|
||||
<div class="section" style="--section-accent:var(--red)">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:var(--red)">## modern_code</span>
|
||||
<span class="from-badge acc-red">Nora "NullX"</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
<strong>Moderne Security nutzt Framework-eigene Controls, anstatt Schutzmechanismen selbst zu bauen.</strong> Deklarative Security-Annotationen sind auditierbar via Reflection — imperativ gestreute if-Checks nicht. Aktuelle Framework-Versionen enthalten Security-Verbesserungen, die ältere Versionen nicht haben. Aktuell bleiben ist eine Sicherheitsstrategie.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- secure_code → UX -->
|
||||
<div class="section" style="--section-accent:var(--purple)">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:var(--purple)">## secure_code</span>
|
||||
<span class="from-badge acc-purple">Leonie Voss</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
UI-Security schützt Nutzer vor schädlichen Interaktionen — irreführende Interfaces, exponierte Daten, unsichtbare Fallen. <strong>Accessible Interfaces sind inhärent sicherer, weil sie State-Änderungen explizit und navigierbar machen.</strong> Sicherheit und Usability sind Verbündete, keine Trade-offs.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- testable_code → UX -->
|
||||
<div class="section" style="--section-accent:var(--purple)">
|
||||
<div class="section-label-col">
|
||||
<span class="section-heading" style="color:var(--purple)">## testable_code</span>
|
||||
<span class="from-badge acc-purple">Leonie Voss</span>
|
||||
</div>
|
||||
<div class="section-text">
|
||||
UI-Code ist testbar, wenn visuelle Zustände verifizierbar sind und Design-Entscheidungen mit exakten Werten dokumentiert sind. <strong>Accessibility muss auf jeder Seite automatisch getestet werden</strong> — manuelle visuelle Prüfungen übersehen Regressionen. Visuelle Regression-Tests bei mehreren Breakpoints fangen Layout-Shifts auf, die kein Unit-Test erkennt.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /content -->
|
||||
</div><!-- /body -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
130
docs/presentation/personas/01-elicit.html
Normal file
130
docs/presentation/personas/01-elicit.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Elicit — Requirements Engineer</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#F59E0B;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">req_engineer.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span class="sep">·</span>
|
||||
<span>mode: BROWNFIELD</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">EL</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Elicit</div>
|
||||
<div class="persona-sub">
|
||||
<span>Requirements Engineer & Business Analyst</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">20+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>BABOK · JTBD · IEEE 830</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"You don't know what you want until you can't articulate what you don't want."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">BABOK v3</span>
|
||||
<span class="skill">JTBD</span>
|
||||
<span class="skill">Story Mapping</span>
|
||||
<span class="skill">Impact Mapping</span>
|
||||
<span class="skill">IEEE 830</span>
|
||||
<span class="skill">Gherkin</span>
|
||||
<span class="skill">5 Whys</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> Strukturiertes Interview führen</li>
|
||||
<li><span class="icon">✓</span> User Journey in Prosa dokumentieren</li>
|
||||
<li><span class="icon">✓</span> E2E-Szenarien (Given/When/Then) schreiben</li>
|
||||
<li><span class="icon">✓</span> Fehlende NFRs und Widersprüche benennen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Produktionscode oder SQL schreiben</li>
|
||||
<li><span class="icon">✗</span> Architektur- oder Hosting-Entscheidungen treffen</li>
|
||||
<li><span class="icon">✗</span> Pixel-genaue Mockups oder Figma-Files liefern</li>
|
||||
<li><span class="icon">✗</span> Frameworks empfehlen (nur als Constraint framen)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">Fehlende User Journeys</span>
|
||||
<span class="review-item">Untestbare Requirements</span>
|
||||
<span class="review-item">Scope Creep</span>
|
||||
<span class="review-item">Fehlende NFRs</span>
|
||||
<span class="review-item">Kein expliziter Out-of-Scope</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
130
docs/presentation/personas/02-markus-keller.html
Normal file
130
docs/presentation/personas/02-markus-keller.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Markus Keller — Architect</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#60A5FA;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">architect.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>philosophy: boring-tech-wins</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">MK</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Markus Keller</div>
|
||||
<div class="persona-sub">
|
||||
<span>Senior Application Architect</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">15+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@mkeller</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"Boring technology wins. Every architecture decision is a trade-off you either make consciously — or regret later."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">C4 Model</span>
|
||||
<span class="skill">DDD</span>
|
||||
<span class="skill">SOLID</span>
|
||||
<span class="skill">ADRs</span>
|
||||
<span class="skill">Modular Monolith</span>
|
||||
<span class="skill">Spring Boot 4</span>
|
||||
<span class="skill">PostgreSQL</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> ADRs vor jeder strukturellen Entscheidung schreiben</li>
|
||||
<li><span class="icon">✓</span> Domain-Grenzen definieren und durchsetzen</li>
|
||||
<li><span class="icon">✓</span> Trade-off-Analysen liefern (nicht nur Meinung)</li>
|
||||
<li><span class="icon">✓</span> Layering-Verletzungen mit konkreter Alternative benennen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Implementierungscode schreiben</li>
|
||||
<li><span class="icon">✗</span> Microservices ohne konkrete Begründung empfehlen</li>
|
||||
<li><span class="icon">✗</span> Test-Implementierungen oder UI-Pixel-Entscheidungen</li>
|
||||
<li><span class="icon">✗</span> „Best practice" als Begründung akzeptieren</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">Layering-Verletzungen</span>
|
||||
<span class="review-item">Cross-Domain-Coupling</span>
|
||||
<span class="review-item">Fehlende ADRs</span>
|
||||
<span class="review-item">Premature Complexity</span>
|
||||
<span class="review-item">Service-Grenzen</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
131
docs/presentation/personas/03-felix-brandt.html
Normal file
131
docs/presentation/personas/03-felix-brandt.html
Normal file
@@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Felix Brandt — Developer</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#34D399;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:18px 40px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:15px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:14px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 40px 28px 40px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:24px;padding-top:28px;padding-bottom:22px}
|
||||
.avatar{width:96px;height:96px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:30px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:42px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:6px;font-family:'JetBrains Mono',monospace;font-size:16px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:14px 20px;margin-bottom:24px;font-style:italic;font-size:19px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:24px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:15px;font-weight:500;padding:5px 14px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:18px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:11px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:16px 18px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:7px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:10px;font-size:17px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:16px 18px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:11px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:16px 20px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:10px 22px;margin-top:9px}
|
||||
.review-item{font-size:16px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:14px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">developer.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>red_step: NEVER_SKIP</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">FB</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Felix Brandt</div>
|
||||
<div class="persona-sub">
|
||||
<span>Senior Fullstack Developer</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">8+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@felixbrandt</span>
|
||||
<span class="sep">·</span>
|
||||
<span>SvelteKit · Spring Boot · TDD</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"If the test was written after the code, it's not a test. It's documentation."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">TDD</span>
|
||||
<span class="skill">Spring Boot 4</span>
|
||||
<span class="skill">SvelteKit 5</span>
|
||||
<span class="skill">Clean Code</span>
|
||||
<span class="skill">SOLID</span>
|
||||
<span class="skill">Refactoring</span>
|
||||
<span class="skill">TypeScript</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> Failing test schreiben — immer zuerst</li>
|
||||
<li><span class="icon">✓</span> Red/Green/Refactor-Zyklus konsequent einhalten</li>
|
||||
<li><span class="icon">✓</span> Test + Implementierung im selben atomaren Commit</li>
|
||||
<li><span class="icon">✓</span> Zeilennummern in jedem Review-Kommentar</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Test nachträglich schreiben um Code zu rechtfertigen</li>
|
||||
<li><span class="icon">✗</span> Red-Schritt überspringen — auch bei „offensichtlichen" Fixes</li>
|
||||
<li><span class="icon">✗</span> Mehrere logische Changes in einem einzigen Commit bündeln</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">TDD-Compliance</span>
|
||||
<span class="review-item">Naming & Abstraktionen</span>
|
||||
<span class="review-item">SOLID-Verletzungen</span>
|
||||
<span class="review-item">Dead Code</span>
|
||||
<span class="review-item">Commit-Struktur</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
129
docs/presentation/personas/04-leonie-voss.html
Normal file
129
docs/presentation/personas/04-leonie-voss.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Leonie Voss — UX Designer</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#A78BFA;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">ui_expert.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>a11y: WCAG_2.1_AA</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">LV</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Leonie Voss</div>
|
||||
<div class="persona-sub">
|
||||
<span>UI/UX Design Lead & Accessibility Advocate</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">12+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@leonievoss</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"Design for the hardest constraint first. If it works for a 67-year-old in bright sunlight, it works for everyone."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">WCAG 2.1 AA</span>
|
||||
<span class="skill">Tailwind CSS 4</span>
|
||||
<span class="skill">Figma</span>
|
||||
<span class="skill">Responsive Design</span>
|
||||
<span class="skill">Brand Systems</span>
|
||||
<span class="skill">ARIA</span>
|
||||
<span class="skill">Svelte 5</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> Kontrast-Verhältnisse messen (min. 4.5:1 Text)</li>
|
||||
<li><span class="icon">✓</span> Touch-Targets auf ≥44px prüfen</li>
|
||||
<li><span class="icon">✓</span> Brand-Compliance (Navy/Mint/Sand) sicherstellen</li>
|
||||
<li><span class="icon">✓</span> Jeden Critique mit konkretem Fix liefern</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Backend-Code oder Datenbank-Design</li>
|
||||
<li><span class="icon">✗</span> Performance-Profiling oder Infrastruktur</li>
|
||||
<li><span class="icon">✗</span> Accessibility-Mängel ohne Fix-Vorschlag benennen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">Kontrast-Verhältnisse</span>
|
||||
<span class="review-item">Touch Targets ≥44px</span>
|
||||
<span class="review-item">Keyboard Navigation</span>
|
||||
<span class="review-item">Responsive Breakpoints</span>
|
||||
<span class="review-item">Fokus-Ringe sichtbar</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
129
docs/presentation/personas/05-sara-holt.html
Normal file
129
docs/presentation/personas/05-sara-holt.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sara Holt — QA Engineer</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#FB923C;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">tester.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>flaky_tests: NOT_ACCEPTABLE</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">SH</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Sara Holt</div>
|
||||
<div class="persona-sub">
|
||||
<span>Senior QA Engineer & Test Automation Specialist</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">10+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@saraholt</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"A passing test that was never failing is a lie with a green checkmark."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">Playwright</span>
|
||||
<span class="skill">Vitest</span>
|
||||
<span class="skill">JUnit 5</span>
|
||||
<span class="skill">Testcontainers</span>
|
||||
<span class="skill">Test Pyramid</span>
|
||||
<span class="skill">Mutation Testing</span>
|
||||
<span class="skill">@WebMvcTest</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> E2E-Szenarien aus Issue-Gherkin ableiten</li>
|
||||
<li><span class="icon">✓</span> Test-Pyramide auf allen drei Ebenen durchsetzen</li>
|
||||
<li><span class="icon">✓</span> Randfälle und Fehlerpfade explizit benennen</li>
|
||||
<li><span class="icon">✓</span> Arrange-Act-Assert in jedem Test sicherstellen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Produktionscode oder Deployments schreiben</li>
|
||||
<li><span class="icon">✗</span> Flaky Tests akzeptieren — auch unter Zeitdruck</li>
|
||||
<li><span class="icon">✗</span> Test-Namen wie <code>test1()</code> oder <code>shouldWork()</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">Fehlende Randfälle</span>
|
||||
<span class="review-item">Flaky-Test-Muster</span>
|
||||
<span class="review-item">Ungetestete Happy Paths</span>
|
||||
<span class="review-item">AAA-Verletzungen</span>
|
||||
<span class="review-item">Kein Red-Beweis</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
135
docs/presentation/personas/06-nora-steiner.html
Normal file
135
docs/presentation/personas/06-nora-steiner.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nora "NullX" Steiner — Security Engineer</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#F87171;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
.cert-badges{display:flex;gap:8px;margin-top:4px}
|
||||
.cert{font-family:'JetBrains Mono',monospace;font-size:10px;padding:2px 8px;border-radius:3px;background:color-mix(in srgb,var(--accent) 15%,transparent);border:1px solid color-mix(in srgb,var(--accent) 40%,transparent);color:var(--accent);font-weight:700}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">security_expert.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>mindset: adversarial</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">N0</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Nora <span style="color:var(--accent)">"NullX"</span> Steiner</div>
|
||||
<div class="persona-sub">
|
||||
<span>Application Security Engineer · Ethical Hacker</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">8+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@nullx</span>
|
||||
</div>
|
||||
<div class="cert-badges">
|
||||
<span class="cert">OSWE</span>
|
||||
<span class="cert">BSCP</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"Trust no one. Validate everything. Especially the things that look safe."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">OWASP Top 10</span>
|
||||
<span class="skill">Spring Security</span>
|
||||
<span class="skill">Burp Suite</span>
|
||||
<span class="skill">SQL Injection</span>
|
||||
<span class="skill">XSS</span>
|
||||
<span class="skill">JWT</span>
|
||||
<span class="skill">CSRF</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> Auth-Audit bei jedem neuen Endpoint</li>
|
||||
<li><span class="icon">✓</span> Jede Lücke mit Exploit-Szenario und Fix liefern</li>
|
||||
<li><span class="icon">✓</span> Sicherheitskommentare mit Threat-Model-Kontext</li>
|
||||
<li><span class="icon">✓</span> Entwickler aufklären — niemals beschämen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Feature-Implementierung oder UI-Entscheidungen</li>
|
||||
<li><span class="icon">✗</span> Security-Findings ohne Fix-Vorschlag posten</li>
|
||||
<li><span class="icon">✗</span> Performance-Optimierungen bewerten</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">@RequirePermission auf jedem Endpoint</span>
|
||||
<span class="review-item">Injection-Vektoren</span>
|
||||
<span class="review-item">Sensitive Data Exposure</span>
|
||||
<span class="review-item">Session Handling</span>
|
||||
<span class="review-item">Actuator-Endpoints</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
129
docs/presentation/personas/07-tobias-wendt.html
Normal file
129
docs/presentation/personas/07-tobias-wendt.html
Normal file
@@ -0,0 +1,129 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tobias "tobi" Wendt — DevOps</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--accent:#22D3EE;
|
||||
--bg:#0D1117;--surface:#161B22;--surface-2:#1C2128;--border:#21262D;
|
||||
--text:#C9D1D9;--text-muted:#6E7681;--text-bright:#F0F6FC;
|
||||
--red:#F85149;--red-bg:rgba(248,81,73,0.07);--red-border:rgba(248,81,73,0.28);
|
||||
}
|
||||
body{background:var(--bg);color:var(--text);font-family:'Inter',system-ui,sans-serif;height:100vh;display:flex;align-items:stretch;padding:14px}
|
||||
.card{width:100%;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative}
|
||||
.card::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,0.05) 1px,transparent 1px);background-size:26px 26px;pointer-events:none}
|
||||
.topbar{display:flex;align-items:center;justify-content:space-between;padding:14px 32px;border-bottom:1px solid var(--border);border-left:4px solid var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text-muted)}
|
||||
.topbar-path .dir{color:var(--text-muted)}.topbar-path .file{color:var(--accent);font-weight:500}
|
||||
.topbar-right{display:flex;align-items:center;gap:20px}
|
||||
.status{display:flex;align-items:center;gap:5px}
|
||||
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pulse 2.5s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||||
.project-label{color:var(--accent);font-weight:700;letter-spacing:1px;font-size:10px}
|
||||
.card-body{border-left:4px solid var(--accent);flex:1;display:flex;flex-direction:column;padding:0 32px 24px 32px;position:relative}
|
||||
.header{display:flex;align-items:center;gap:20px;padding-top:24px;padding-bottom:20px}
|
||||
.avatar{width:72px;height:72px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);border:2px solid var(--accent);display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-size:22px;font-weight:700;color:var(--accent);flex-shrink:0;letter-spacing:-1px}
|
||||
.persona-name{font-size:30px;font-weight:800;color:var(--text-bright);letter-spacing:-.5px;line-height:1.1}
|
||||
.persona-sub{display:flex;align-items:center;gap:10px;margin-top:5px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--text-muted)}
|
||||
.persona-sub .accent{color:var(--accent)}.persona-sub .sep{color:var(--border)}
|
||||
.quote{border-left:3px solid var(--accent);padding:10px 16px;margin-bottom:20px;font-style:italic;font-size:13.5px;line-height:1.65;color:var(--text-muted);background:color-mix(in srgb,var(--accent) 5%,transparent);border-radius:0 6px 6px 0}
|
||||
.skills-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:20px}
|
||||
.skill{font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:500;padding:3px 10px;border-radius:4px;border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent)}
|
||||
.columns{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||||
.section-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:9px}
|
||||
.does-box{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:13px 14px}
|
||||
.does-box .section-label{color:var(--accent)}
|
||||
.item-list{list-style:none;display:flex;flex-direction:column;gap:5px}
|
||||
.item-list li{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;line-height:1.4;color:var(--text)}
|
||||
.item-list li .icon{flex-shrink:0}
|
||||
.does-box .icon{color:var(--accent)}
|
||||
.never-box{background:var(--red-bg);border:1px solid var(--red-border);border-radius:7px;padding:13px 14px}
|
||||
.never-label{font-family:'JetBrains Mono',monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--red);margin-bottom:9px;display:flex;align-items:center;gap:6px}
|
||||
.never-box .icon{color:var(--red)}
|
||||
.review-box{background:color-mix(in srgb,var(--accent) 7%,transparent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:7px;padding:12px 16px}
|
||||
.review-items{display:flex;flex-wrap:wrap;gap:8px 18px;margin-top:7px}
|
||||
.review-item{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:5px}
|
||||
.review-item::before{content:'→';color:var(--accent);font-size:11px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="topbar">
|
||||
<div class="topbar-path">
|
||||
<span class="dir">.claude/personas/</span><span class="file">devops.md</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<div class="status"><div class="status-dot"></div><span>active</span></div>
|
||||
<span>·</span>
|
||||
<span>complexity: LIABILITY_NOT_FEATURE</span>
|
||||
<span>·</span>
|
||||
<span class="project-label">FAMILIENARCHIV</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="header">
|
||||
<div class="avatar">TW</div>
|
||||
<div class="name-block">
|
||||
<div class="persona-name">Tobias <span style="color:var(--accent)">"tobi"</span> Wendt</div>
|
||||
<div class="persona-sub">
|
||||
<span>DevOps & Platform Engineer</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="accent">10+ Jahre XP</span>
|
||||
<span class="sep">·</span>
|
||||
<span>@tobiwendt</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quote">
|
||||
"Every added tool is a new failure mode. If it's not in Docker Compose, it doesn't exist."
|
||||
</div>
|
||||
|
||||
<div class="skills-row">
|
||||
<span class="skill">Docker Compose</span>
|
||||
<span class="skill">Gitea Actions</span>
|
||||
<span class="skill">PostgreSQL Ops</span>
|
||||
<span class="skill">MinIO</span>
|
||||
<span class="skill">Caddy</span>
|
||||
<span class="skill">Renovate</span>
|
||||
<span class="skill">Flyway</span>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="does-box">
|
||||
<div class="section-label">// does</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✓</span> Docker Image-Tags auf spezifische Versionen pinnen</li>
|
||||
<li><span class="icon">✓</span> Health Checks für jeden Service definieren</li>
|
||||
<li><span class="icon">✓</span> Rollback-Strategie in jedem Deployment-Change</li>
|
||||
<li><span class="icon">✓</span> Komplexität mit konkreter Begründung rechtfertigen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="never-box">
|
||||
<div class="never-label">⊘ hard limits</div>
|
||||
<ul class="item-list">
|
||||
<li><span class="icon">✗</span> Anwendungscode oder Business-Logik schreiben</li>
|
||||
<li><span class="icon">✗</span> Kafka/RabbitMQ empfehlen bevor PostgreSQL ausgeschöpft</li>
|
||||
<li><span class="icon">✗</span> Microservices ohne messbaren Scaling-Bedarf</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="review-box">
|
||||
<div class="section-label">// review focus</div>
|
||||
<div class="review-items">
|
||||
<span class="review-item">Fehlende Health Checks</span>
|
||||
<span class="review-item">Hardcoded Secrets</span>
|
||||
<span class="review-item">Ungepinnte Image-Tags</span>
|
||||
<span class="review-item">Kein Rollback-Plan</span>
|
||||
<span class="review-item">Premature Infra-Complexity</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
241
docs/presentation/personas/_card.css
Normal file
241
docs/presentation/personas/_card.css
Normal file
@@ -0,0 +1,241 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--bg: #0D1117;
|
||||
--surface: #161B22;
|
||||
--surface-2: #1C2128;
|
||||
--border: #21262D;
|
||||
--text: #C9D1D9;
|
||||
--text-muted: #6E7681;
|
||||
--text-bright: #F0F6FC;
|
||||
--red: #F85149;
|
||||
--red-bg: rgba(248,81,73,0.07);
|
||||
--red-border: rgba(248,81,73,0.28);
|
||||
/* --accent set per file */
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
/* ── Card shell ── */
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 1080px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* dot grid */
|
||||
.card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(circle, rgba(255,255,255,0.06) 1px, transparent 1px);
|
||||
background-size: 26px 26px;
|
||||
pointer-events: none;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* accent left stripe */
|
||||
.card-body {
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0 32px 28px 32px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ── Top bar ── */
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 32px 14px 32px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
border-left: 4px solid var(--accent);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.topbar-path { display: flex; align-items: center; gap: 6px; }
|
||||
.topbar-path .dir { color: var(--text-muted); }
|
||||
.topbar-path .file { color: var(--accent); font-weight: 500; }
|
||||
|
||||
.topbar-right { display: flex; align-items: center; gap: 20px; }
|
||||
.status { display: flex; align-items: center; gap: 5px; }
|
||||
.status-dot {
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 6px var(--accent);
|
||||
animation: pulse 2.5s ease-in-out infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%,100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
.project-label { color: var(--accent); font-weight: 700; letter-spacing: 1px; font-size: 10px; }
|
||||
|
||||
/* ── Header ── */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
||||
border: 2px solid var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
flex-shrink: 0;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.name-block {}
|
||||
.persona-name {
|
||||
font-size: 30px;
|
||||
font-weight: 800;
|
||||
color: var(--text-bright);
|
||||
letter-spacing: -0.5px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.persona-sub {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 5px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.persona-sub .accent { color: var(--accent); }
|
||||
.persona-sub .sep { color: var(--border); }
|
||||
.persona-sub .handle { color: var(--text-muted); }
|
||||
|
||||
/* ── Quote ── */
|
||||
.quote {
|
||||
border-left: 3px solid var(--accent);
|
||||
padding: 10px 16px;
|
||||
margin-bottom: 20px;
|
||||
font-style: italic;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.65;
|
||||
color: var(--text-muted);
|
||||
background: color-mix(in srgb, var(--accent) 5%, transparent);
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
.quote em { color: color-mix(in srgb, var(--accent) 80%, white); font-style: normal; }
|
||||
|
||||
/* ── Skills ── */
|
||||
.skills-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.skill {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
padding: 3px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
|
||||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Two-column ── */
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.5px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
|
||||
.does-box {
|
||||
background: var(--surface-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 7px;
|
||||
padding: 13px 14px;
|
||||
}
|
||||
.does-box .section-label { color: var(--accent); }
|
||||
|
||||
.item-list { list-style: none; display: flex; flex-direction: column; gap: 5px; }
|
||||
.item-list li {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
font-size: 12.5px; line-height: 1.4; color: var(--text);
|
||||
}
|
||||
.item-list li .icon { flex-shrink: 0; font-style: normal; }
|
||||
.does-box .icon { color: var(--accent); }
|
||||
|
||||
.never-box {
|
||||
background: var(--red-bg);
|
||||
border: 1px solid var(--red-border);
|
||||
border-radius: 7px;
|
||||
padding: 13px 14px;
|
||||
}
|
||||
.never-label {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.5px;
|
||||
color: var(--red);
|
||||
margin-bottom: 9px;
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.never-box .icon { color: var(--red); }
|
||||
|
||||
/* ── Review Focus ── */
|
||||
.review-box {
|
||||
background: color-mix(in srgb, var(--accent) 7%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
|
||||
border-radius: 7px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.review-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 18px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
.review-item {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
display: flex; align-items: center; gap: 5px;
|
||||
}
|
||||
.review-item::before { content: '→'; color: var(--accent); font-size: 11px; }
|
||||
Reference in New Issue
Block a user