Files
kanban/SPEC.md
Robin Choice 02ac470eff Initial commit — Kanban-App migriert von ~/.kanban nach ~/dev/kanban
Self-contained HTML-Kanban als operative Single Source of Truth
für alle laufenden Projekte. Visualisiert das Ringsystem.

Stack: Vanilla HTML/CSS/JS, localStorage, kein Build, kein Backend
Server: ~/dev/kanban/server.sh (Python http.server auf Port 8765)
Spec:   ~/dev/kanban/SPEC.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 23:30:19 +02:00

22 KiB
Raw Permalink Blame History

Kanban — Spec & Architektur

Datei: /Users/robinchoice/dev/kanban/index.html (eine Datei, kein Build) Server: /Users/robinchoice/dev/kanban/server.sh — static-file server aus ~ auf Port 8765 Aufruf: http://localhost:8765/dev/kanban/index.html (Server muss laufen für MD-Viewer; sonst funktioniert das Board trotzdem) State: Browser localStorage


Zweck

Single Source of Truth für alle laufenden Projekte. Self-contained HTML mit Kanban-Methodik (3 Spalten, Expedite-Swimlane, Class of Service, WIP-Limits, Little's Law, Monte Carlo). Visualisiert das Ringsystem von Robin (Ring 03, vier Workspace-Gruppen, ~16 Boards).


Layout

┌──────────────┬──────────────────────────────────────┬──────────────┐
│  SIDEBAR     │  MAIN (Overview / Board / Analytics) │  IDEEN       │
│  links       │                                      │  rechts      │
│              │                                      │              │
│  Gruppen     │  • Header (Titel, Goal, Pills)       │  • Input     │
│  + Boards    │  • Tabs                              │  • Filter    │
│  + Aktionen  │  • View-Body                         │  • Liste     │
│  210 px      │  flex:1                              │  280 px      │
└──────────────┴──────────────────────────────────────┴──────────────┘

Banner oben (rot/amber) wenn auf file:// statt localhost:8765 — mit Switch-Button.


Drei Views (Tabs in der Reihenfolge: Overview · Board · Analytics)

Overview (Default)

Hero-Block + 5 Sektionen, alle Felder editierbar:

Element Quelle Edit
Hero-Icon (96×96, rounded) overview.icon (path) bei Bild-Fail Emoji-Fallback in Board-Farbe
Hero-Name board.name im Board-Header editierbar
Hero-Tagline (lila, kursiv) overview.tagline Click + tippen
Hero-Description (2-3 Sätze) overview.description Click + tippen
Badges: Ring, Gruppe, Typ live + overview.type type editierbar
Launch-Cards Grid overview.launches[] Single-Click öffnet URL (oder MD-Viewer wenn .md). Doppelklick auf Text editiert. × löscht.
Was bisher geschah overview.summary Click + tippen
Tech-Stack overview.stack[] Pill mit ×, + Tech Input
Hosting · IDs · Pfade overview.info[] Label + Value editierbar, Copy-Btn, ×, + Zeile
🔒 Secrets · Keys overview.secrets[] Label + Value editierbar, masked-Toggle (Auge), Copy-Btn, ×, + Secret
⌘ Quick-Commands overview.commands[] Label + Cmd editierbar, Copy-Btn, ×, + Command

Board

3-Spalten-Kanban + Expedite-Swimlane darüber.

Spalte Farbe WIP-Limit
Ready amber
In Progress blau per Board (Pill im Header editierbar)
Done grün

Expedite-Swimlane: horizontale Reihe über dem Board mit 3 Zonen (deckungsgleich mit Spalten). Tasks mit cos:'expedite' erscheinen NUR in der Zone, nicht im normalen col-body. Drag zwischen Zonen behält Expedite. Drag in col-body demotet. Drag von col-body in Zone promoted.

Karten-Aktionen (sichtbar bei Hover, oben rechts):

  • ⚑ Blocker togglen (task.blocked) — Karte rot
  • ↗ Promote-to-Board — öffnet Add-Board-Modal mit Task-Titel vorbelegt
  • × Löschen

Auto-Aging: Karte > SLE-Tage in einer Spalte → roter Glow + "ÜBERFÄLLIG"-Label. Done-Karten werden nicht alt gezeigt.

Add-Card: + Karte… Input am Spalten-Ende. Enter fügt hinzu, Esc blurt.

Analytics

KPI Berechnung Farbe
WIP count(wip) gegen wipLimit rot/amber/blau
Cycle Time ⌀ WIP / Throughput × 7 (Tage) blau
Queue ETA (WIP+Ready) / Throughput × 7 (Tage bis Queue leer) teal
Flow Efficiency WIP / (WIP+Ready) × 100% grün/amber/rot
Remaining WIP + Ready neutral

Throughput-Quelle: automatisch realThroughput() aus Done-Cards der letzten 14 Tage (mit task.doneAt). Fallback board.throughput wenn keine History. Status-Pill ("↻ Real" grün vs. "⊘ Statisch" grau).

Monte Carlo: 8.000 Runs, Varianz ±60%, gibt P50/P75/P85/P95 Wochen + Datum. Live-Input für Throughput-Override.

Active Blockers: alle Tasks mit task.blocked === true.

SLE: vergleicht Cycle Time mit board.sle.days.


Gruppen-Sections, dynamisch aus GROUPS + BOARDS gerendert. Meta-Gruppe (Ring 0) ist visuell hervorgehoben (lila Gradient, Glow, -Bullet).

Element Verhalten
Gruppen-Section Drop-Zone für Board-Drag (board.group ändern)
Gruppen-Header (s-group) draggable — Reihenfolge der Gruppen ändern (persistiert in kanban_groups)
Nav-Item (Board) Klick = aktivieren; draggable in andere Gruppe oder vor andere Nav-Items (Reihenfolge in kanban_board_order)
Ring-Badge (R0/R1p/R1w/R2/R3/?) Klick togglet durch Ring-Werte
Badge (Count) remaining(b) mit Color-Code (rot=Blocker, blau=WIP, grau=neutral)
× (nur user-erstellt) löscht das Board

Unten:

  • + Board → Modal: Name, Goal, Gruppe (oder + Neue Gruppe), Farb-Picker (8 Optionen)
  • + Gruppeprompt() für Namen; leere Gruppen werden gezeigt
  • ↺ Reset → aktuelles Board auf DEFAULTS zurück; bei user-erstellten Boards: löschen

Ideen-Pinnwand (rechts)

Element Verhalten
Input + Enter neue Idee am Anfang einfügen, Tag aus aktivem Filter
Filter-Buttons (Alle/Projekt/Erkenntnis/Später) filtern Liste + setzen Default-Tag
Idee-Card hover-Buttons: (Promote-Modal) und (löschen); draggable auf col-body → erstellt Task
Drop-Zonen (erscheinen beim Card-Drag aus Board) 📂 Projekt / 💡 Erkenntnis / 🕒 Später / ○ ohne Label — droppen entfernt Task aus Board und legt Idee an

Seed: 35 Ideen (v1=20 Projekt, v2=15 Erkenntnis, v3=+16 aus _archive). Versionierung via kanban_ideas_seeded Key.


Datenmodell

Task

{
  t: 'Titel',                       // required
  blocked: true,                    // optional — Karte rot, ⚑ aktiv
  cos: 'expedite' | 'fixed',        // optional — Class of Service
  movedAt: 1779580800000,           // Timestamp letzter Spaltenwechsel (für Aging)
  doneAt: 1779580800000,            // Timestamp bei Wechsel nach Done (für Throughput)
  age: 14,                          // optional — manuell überschriebenes Aging (rare)
  note: 'Notiz',                    // optional — graue Sub-Zeile
}

Board

{
  name: 'Döner-App',
  goal: 'Beschreibung',
  group: 'Code',                    // Sidebar-Gruppe
  color: '#f87171',                 // Dot-Farbe
  ring: '0' | '1p' | '1w' | '2' | '3' | '?',
  wipLimit: 3,                      // editierbar via Pill
  throughput: 2,                    // editierbar via Pill (Fallback wenn keine Done-History)
  sle: { days: 14, p: 85 },         // days editierbar via Pill
  cols: [
    { id: 'ready', label: 'Ready',       tasks: [...] },
    { id: 'wip',   label: 'In Progress', tasks: [...] },
    { id: 'done',  label: 'Done',        tasks: [...] },
  ],
  focus: 'Fixstern-Text',           // editierbar im Header
  overview: {                        // optional
    icon: 'assets/...png',
    tagline: '...',
    description: '...',
    summary: '...',
    type: 'ios-app' | 'web' | 'meta' | ...,
    launches: [{label, sub, url, icon}],
    stack: ['...'],
    info: [{label, value}],
    secrets: [{label, value, masked}],
    commands: [{label, cmd}],
  },
  userCreated: true,                // nur bei UI-erstellten Boards
}

Idea

{
  id: 1013,
  text: '...',
  tag: 'projekt' | 'erkenntnis' | 'spaeter',  // optional
  created: '17.05.' | 'archive/...' | 'journal/...',
}

State-Keys in localStorage

Key Inhalt
kanban_v2 {[boardId]: {cols, focus, [overrides], [userCreated-Meta]}}
kanban_groups ['Meta','Code',...]
kanban_board_order {[group]: [id1, id2, ...]}
kanban_ideas [Idea, ...]
kanban_ideas_seeded 'v3'

Default-Boards

ID Name Gruppe Ring
ringsystem Ringsystem Meta 0
kanban Kanban (dieses Board) Meta 0
doener Döner-App Code 3
musichub Music Hub Code 1w
openclaw OpenClaw / Rob Code 1w
docpilot docpilot Code 1w
k4 K4 Digital — PM-Mandat Beruflich 1w
branding Branding & Außendarstellung Beruflich 1w
psk PSK I Zertifizierung Beruflich 1w
eu Einzelunternehmen Beruflich 1w
pleasance Pleasance Web 3
mdim mydrugismusic Website Web 3
privat Haushalt & Leben Privat 1p
degoogle De-Google / FOSS Migration Privat 1p
bibliothek Bibliothek-Pipeline Privat 1p
aikb AI Engineering KB Privat 1w

Overview gefüllt für: doener, kanban, ringsystem. Andere: nur Skeleton (Pilot-Modus).


Tool-Agnostik

Datei Status
~/.claude/AGENTS.md kanonisch
~/.claude/CLAUDE.md Symlink → AGENTS.md
~/dev/personal-vault/AGENTS.md kanonisch
~/dev/personal-vault/CLAUDE.md Symlink → AGENTS.md
~/dev/robin-work/AGENTS.md kanonisch
~/dev/robin-work/CLAUDE.md Symlink → AGENTS.md

Backup: ~/.claude/CLAUDE.md.bak (alte Datei vor der Symlink-Migration)

Skills tool-agnostisch über ~/.skills/{name}.md mit Symlinks aus tool-spezifischen Verzeichnissen (Claude: ~/.claude/skills/{name}/SKILL.md; OpenCode: ~/.config/opencode/commands/{name}.md).


Local-Server

~/dev/kanban/server.sh

Startet python3 -m http.server 8765 --bind 127.0.0.1 aus ~/.

  • Aufruf: ~/dev/kanban/server.sh (Vordergrund) oder nohup ~/dev/kanban/server.sh & (Hintergrund persist)
  • Stop: pkill -f "http.server 8765"
  • Logs: /tmp/kanban-server.log (wenn via nohup gestartet)

Benötigt nur für MD-Viewer und localhost-URLs. Board-Funktion funktioniert auch ohne Server unter file://. Aber dann erscheint ein Warn-Banner oben.


MD-Viewer

Klick auf Launch-Card mit .md-URL → Modal mit gerendertem Markdown.

Mini-Parser inline: Headings (h1-h3), Bold, Italic, inline Code, Code-Blocks, Listen (ul/ol), Tables, Blockquotes, Hr, Links.

Fallback wenn fetch() scheitert (CORS): Anleitung mit drei Optionen (Extension / Server / Tab).


Wichtige JS-Funktionen

Render

Funktion Beschreibung
show(id) Board wechseln
setView(v) Tab-Switch (overview/board/analytics)
renderOverview(id) Hero + 5 Sektionen
renderBoard(id) Swimlane + 3 Spalten + col-add-input
renderAnalytics(id) KPIs, Flow, Monte Carlo, Blockers, SLE
renderSidebar() Gruppen + Boards
renderIdeas() gefilterte Ideen mit Drag-Attrs
initBadges() Alias renderSidebar
renderMarkdown(src) Mini-MD-Parser

State

Funktion Beschreibung
loadState() / saveState() BOARDS persist mit Override-Logik für Defaults
loadGroups() / saveGroups() + Migration: Meta vorne einfügen wenn fehlend
loadBoardOrder() / saveBoardOrder() per-Group-Order
loadIdeas() / saveIdeas() + Seeding
migrateCols(b) 5→3-Spalten-Migration
getBoardsForGroup(g) geordnete Board-IDs

Mutations (Board)

Funktion Beschreibung
addCard(boardId, colId, text) mit movedAt + ggf. doneAt
deleteCard(boardId, colId, idx)
toggleBlocker(boardId, colId, idx) task.blocked
saveBoardSetting(key, el, min, max) wipLimit/throughput/sleDays
saveBoardName() / saveBoardGoal() Header-Edit
saveFocus() Fokus-Text im Header
resetCurrentBoard() Default-Reset oder User-Board-Delete
deleteBoard(id, e) nur user-erstellt
cycleRing(boardId, e) togglet durch Ring-Werte
promoteToBoard(boardId, colId, idx) Task → neues Board

Mutations (Overview)

Funktion Beschreibung
updateOvFromEl(el) aus data-section/idx/prop
addOvRow(section) leere Zeile für info/secrets/launches/commands
addOvPill(section, value) für stack
deleteOvRow(section, idx)
enableEdit(el) Launch-Card-Felder Doppelklick aktiviert
toggleSecretBtn(btn) masked toggle
copyText(t, btn) / copyFromBtn(btn) Clipboard mit visual feedback

Mutations (Ideen)

Funktion Beschreibung
addIdea() / deleteIdea(idx)
filterIdeas(tag) + setzt Default-Tag
openPromote(idx) / confirmPromote() / closePromote() Modal Idee→Task

Add-Board-Modal

Funktion Beschreibung
showAddBoard() / closeAddBoard()
selectAbColor(c, el) Farb-Picker
confirmAddBoard() + Promote-Source-Handling
showAddGroup() prompt()-Dialog

DnD

Funktion Beschreibung
onDragStart/End (Cards) + showIdeaDropZones(on)
onDragOver/Drop (col-body) demotet expedite, handelt auch Ideen-Drops
onDragOverExp/onDropToExpCol promoted expedite
onBoardDragStart/End Board-Drag in Sidebar
onGroupDragOver/Drop Board → Gruppe
onNavItemDragOver/Drop Board-Reorder innerhalb Gruppe
onIdeaDragStart/End Idee aus Pinnwand
onGroupHeaderDragStart/End Gruppe-Reorder
onGroupSectionDragOver/Drop Gruppe-Drop-Target
onIdeaZoneDragOver / onDropToIdeas Card → Idee
onLaunchClick(e, card) Launch-Card öffnet URL/MD

Helper

Funktion Beschreibung
escapeHtml(s) HTML-Escape
liveAge(task) aus movedAt
ageClass(days) fresh/ok/warn/old
colCount(b, id) tasks.length
remaining(b) ready + wip
hasBlocker(b) any task.blocked
monteCarlo(left, tp, runs) {p50,p75,p85,p95}
realThroughput(b, days) aus Done-History
openMD(url) / closeMD() / openMdExternal() MD-Viewer
enableEdit(el) Launch-Card-Edit-Modus aktivieren

Globals

let curId = 'doener';
let curView = 'overview';       // default
let BOARDS = {};                 // runtime, mutiert
let IDEAS = [];
let GROUPS = ['Meta','Code',...];
let BOARD_ORDER = {};
let dragSrc = null;              // {boardId, colId, idx} bei Card-Drag im Board
let dragBoard = null;            // boardId bei Sidebar-Drag
let dragIdea = null;             // idx bei Ideen-Drag
let dragGroup = null;            // group bei Gruppe-Drag
let promoteSource = null;        // {boardId, colId, idx} beim Task→Board
let abSelColor = '#7c6af7';
let promoteIdx = null;
let mdCurrentUrl = null;

CSS-Variablen (Theme)

--bg:#0d0d0f, --surface:#16161a, --surface2:#1e1e24, --surface3:#26262e
--border:#2a2a35, --border2:#353545
--text:#e8e8f0, --text-muted:#6b6b80, --text-dim:#4a4a5a
--accent:#7c6af7 (lila), --accent-soft:#2d2550
--green:#4ade80, --amber:#fbbf24, --red:#f87171, --blue:#60a5fa
--teal:#2dd4bf, --purple:#c084fc, --orange:#fb923c
--sidebar-w:210px, --col-w:240px

Color-of-State Konvention:

  • Ready = amber
  • In Progress = blau
  • Done = grün
  • Blocked = rot (Border + Background-Tint)
  • Expedite = rot (border-left + Swimlane-Background)
  • Fixed Date = amber (border-left)
  • Aging > SLE = roter Glow

Test-Checkliste (für externe Tester-Agents)

Layout & Navigation

  • Sidebar links: alle 5 Gruppen sichtbar (Meta · Code · Beruflich · Web · Privat)
  • Meta-Gruppe: lila Gradient mit -Bullet
  • Klick auf jedes Board lädt korrektes Layout, Header zeigt Name, Goal, Pills (WIP-Limit/TP/SLE), Fokus-Stern
  • Tab-Reihenfolge: Overview · Board · Analytics
  • Default-Tab beim Start: Overview
  • Banner oben wenn auf file:// (nicht localhost:8765)

Boards

  • + Board öffnet Modal mit Name (Pflicht), Goal, Gruppe-Select (+ "Neue Gruppe…"), Farb-Picker (8 Farben)
  • User-Board zeigt × bei Hover (Default-Boards nicht)
  • + Gruppe legt leere Gruppe an (sichtbar mit "Leer — Board hinzufügen" Placeholder)
  • Board-Drag in andere Gruppe → Group-Wechsel persistent
  • Board-Drag auf konkretes Nav-Item → Reihenfolge innerhalb Gruppe persistent
  • Gruppe-Header draggable → Gruppen-Reihenfolge ändert
  • Ring-Badge (R0/R1p/R1w/R2/R3) togglet bei Klick durch alle Werte
  • ↺ Reset: bei Default-Board Spalten reset, bei User-Board Löschen-Confirm
  • Board-Name + Goal per Klick im Header editierbar (Enter speichert, Esc revert)

Settings (Pills im Header)

  • WIP-Limit Pill editierbar (1-20)
  • Throughput Pill editierbar (0.5-30, Dezimalen erlaubt)
  • SLE-Tage Pill editierbar (1-180)
  • Alle drei persistent nach Reload

Karten (Board-View)

  • + Karte… Input: Enter fügt hinzu, Esc blurt
  • Hover-Buttons: ⚑ togglet blocked (Karte rot), ↗ öffnet Promote-Modal, × löscht
  • Drag col-body → col-body: verschiebt + setzt movedAt
  • Drag col-body → exp-zone: verschiebt + setzt cos=expedite
  • Drag exp-zone → exp-zone: verschiebt + bleibt expedite
  • Drag exp-zone → col-body: demotet (cos entfernt)
  • Drag in done setzt zusätzlich doneAt
  • Karte > SLE-Tage in Spalte (außer done): rot leuchtender Glow + "ÜBERFÄLLIG"-Label
  • Drag Card → eine der vier Ideen-Drop-Zonen (rechte Sidebar): erstellt Idee, entfernt Card

Promote-to-Board

  • ↗ auf Karte öffnet Modal mit Name = Task-Titel
  • Hint zeigt Quell-Board und -Spalte korrekt
  • Gruppe + Farbe vorbelegt mit Quell-Board
  • Confirm → neues User-Board, Task aus Quelle entfernt
  • Cancel → kein Board angelegt, Quell-Task bleibt

Overview

  • Hero-Block: Icon (oder Emoji-Fallback in Board-Farbe), Name, Tagline, Description, Badge-Reihe (Ring/Gruppe/Typ)
  • Tagline + Description + Type per Single-Click editierbar (Click + tippen)
  • Launch-Cards: Single-Click öffnet URL (oder MD-Viewer bei .md-URL); Doppelklick auf Text aktiviert Edit-Modus
  • × Entfernen löscht Launch-Card
  • + Launch-Card fügt leere Card hinzu
  • Was bisher geschah: per Click editierbar
  • Tech-Stack-Pills: × löscht, + Tech Input fügt hinzu
  • Info-Rows: Label + Value editierbar, Copy-Btn (✓ Feedback), × löscht, + Zeile fügt hinzu
  • Secrets: Label + Value editierbar, Auge-Toggle (masked persistent), Copy-Btn, ×, + Secret
  • Quick-Commands: Label + Cmd editierbar, Copy-Btn, ×, + Command

MD-Viewer

  • Voraussetzung: Aufruf via http://localhost:8765/dev/kanban/index.html (Server läuft)
  • Klick auf .md-Launch-Card öffnet Modal mit gerendertem Markdown (Headings, Code, Tables, Listen)
  • ↗ Tab öffnet Roh-Inhalt in neuer Tab
  • × Schließen oder Klick auf Overlay schließt Modal
  • Bei file://-Aufruf: Fallback-Anleitung erscheint mit drei Optionen

Analytics

  • Tab-Wechsel ohne State-Verlust
  • WIP-KPI färbt rot bei > wipLimit, amber bei = wipLimit, blau sonst
  • Cycle Time, Queue ETA, Flow Efficiency mathematisch plausibel (siehe Spec oben)
  • Monte Carlo Throughput-Input updated Live
  • Status-Pill grün "↻ Real" wenn Done-Cards mit doneAt < 14d existieren, sonst grau "⊘ Statisch"
  • Active Blockers Panel listet alle Tasks mit blocked:true
  • Flow Distribution: 3 Balken (Ready/WIP/Done) korrekt skaliert

Ideen

  • Input + Enter fügt Idee am Anfang ein
  • Filter-Buttons: ändern Anzeige UND Default-Tag für neue Ideen
  • Hover-Buttons: Promote-Modal (Spalten-Default = erste verfügbare, nie 'backlog'), löscht
  • Drag Idee in col-body → Task entsteht, Idee verschwindet
  • Drag funktioniert nur auf col-body, nicht in exp-zone

Persistenz

  • Reload behält: Boards, Spalten-States, Fokus, Ideen, Gruppen, Group-Order, alle Overrides (name/goal/wipLimit/tp/sle/overview)
  • localStorage.clear() + Reload → alle Defaults zurück inkl. Seed-Ideen v3
  • Alte 5-Spalten-States werden via migrateCols zu 3-Spalten konsolidiert

Tool-Agnostik

  • ~/.claude/CLAUDE.md ist Symlink auf AGENTS.md
  • ~/.claude/CLAUDE.md.bak enthält den alten Inhalt vor Migration
  • Ringsystem-Board zeigt alle relevanten MD-Files als Launch-Cards mit korrekten http://localhost:8765-URLs

Bekannte Einschränkungen

  • Karten-Sortierung INNERHALB einer Spalte nicht möglich (immer append)
  • Card-Edit (Titel/Note ändern) nicht möglich — nur löschen + neu
  • Gruppen können nicht umbenannt oder gelöscht werden
  • Kein Card-Detail-View (kein Comments, Attachments, History)
  • Kein Export
  • Keine Tastatur-Shortcuts
  • localStorage nicht synchronisiert — pro Browser/Gerät separat
  • Keine Undo-Funktion
  • CFD-Chart noch nicht implementiert (Ready-Task)
  • Throughput-History akkumuliert sich erst über Zeit — bei frischem Start fallback auf statischen Wert

Datei-Struktur

/Users/robinchoice/dev/kanban/
├── index.html        # Die gesamte App
├── SPEC.md           # Diese Datei
├── server.sh         # Local HTTP Server (chmod +x)
└── assets/
    └── doener.png    # App-Icon (kopiert aus ~/dev/doener-app/iOS/.../AppIcon.appiconset/icon-1024.png)

Setup (für neue Session / anderen Agent)

# 1. Server starten (für MD-Viewer)
nohup ~/dev/kanban/server.sh > /tmp/kanban-server.log 2>&1 &

# 2. Im Browser öffnen
open http://localhost:8765/dev/kanban/index.html

# 3. Stop wenn nötig
pkill -f "http.server 8765"

Wenn Server nicht laufen soll: einfach open ~/dev/kanban/index.html — Board funktioniert, aber MD-Viewer-Modal zeigt Fallback-Anleitung statt gerenderter Inhalte.