Projekt-Dashboard — Obsidian Bases Karten-View

Overview

Eine Karten-View in Obsidian Bases die alle aktiven Projekt-_index.md als Grid rendert. Ein lokaler launchd-Cron auf Marvins iMac scannt 2-3x täglich git log (Vault + verlinkte Code-Repos) und Claude-Code-Session-Files in ~/.claude/projects/, aktualisiert dabei Aktivitäts-Felder im jeweiligen Projekt-Frontmatter. Marvin pflegt nur next_step manuell — alles andere fliesst.

Problem Frame

Marvin verliert den Überblick über 18+ parallele Projekte und verteilte Claude-Code-Sessions. Daten existieren bereits (Frontmatter, Git-Log, Session-Files), aber ohne aggregierte Sicht. Daily-Briefing ist für den Tagesplan, nicht für „was läuft eigentlich gerade in welchem Projekt”. ADHS-Constraint: System muss unsichtbar laufen, keine Pflege-Disziplin erzwingen. Siehe origin: _index.md.

Requirements Trace

  • R1 Bases-View dashboard.base rendert alle Projekte als Karten-Grid → Unit 6
  • R2 Karten zeigen Status, nächster Schritt, last_activity, sessions_24h, Kunde → Unit 1 (Schema) + Unit 6 (Layout)
  • R3 Klick auf Karte öffnet _index.md → Unit 6
  • R4 Filter zeigt nur status != archiviert/done → Unit 6
  • R5 Default-Sort nach last_activity_at absteigend → Unit 6
  • R6 Frontmatter-Erweiterung um Activity-Felder → Unit 1
  • R7 next_step bleibt manuell → Unit 4 (Writer überschreibt es nie)
  • R8 Cron läuft 2-3x täglich → Unit 5
  • R9 Datenquellen MVP: git log + Claude-Sessions → Unit 2 + Unit 3
  • R10 Frontmatter-Updates dürfen committet werden (optional, Phase 2) → Open Question, default OFF im MVP
  • R11 code_repos: [<slug>...] mappt Projekt zu Source-Repos → Unit 1 + Unit 2

Scope Boundaries

  • Kein Cross-Project-Kanban, keine Timeline-View, keine Live-Ops-Sicht
  • Kein Web-Dashboard, kein Mac-Widget, kein Mobile
  • Keine Telegram/Kalender/Mail-Quellen im MVP (Phase 2/3)
  • Kein Auto-Generieren von next_step
  • Kein Git-Commit aus dem Cron im MVP (siehe Open Question)
  • Keine Backwards-Compat-Migration alter Frontmatter-Felder (nur additive Ergänzung)

Context & Research

Relevant Code and Patterns

  • _index.md — Frontmatter-Pattern für Projekte (id, type: project, status, description, related)
  • _index.md — komplexere Variante mit customer, phase, budget
  • lokale-source-repos.md — kanonische Liste der lokalen Code-Repos (Strategie-Kern, MCPs, Kunden)
  • schemas.md — Frontmatter-Schemas pro Entity-Typ; muss um neue Felder ergänzt werden
  • agents-platform — Cron-Pattern (Lambda), nicht für diesen Plan verwendet weil Sessions lokal liegen

Frontmatter-Variabilität: Aktive Projekte nutzen unterschiedliche Title-Felder (title, name) und unterschiedliche id-Prefixes (proj-YYYY-NNN, proj-<slug>, doc-...). Der Scanner muss tolerant lesen.

Institutional Learnings

  • Marvins ADHS-Pattern (Memory): „System möglichst unsichtbar”. Heisst: kein Approval-Dialog im Cron, keine Notifications für Routine-Updates, kein Pflege-Zwang
  • Vault-Regel 18: NIE in ~/.claude/projects/memory/ schreiben — Cron schreibt nur in Vault-Files
  • Vault-Konvention: Wikilinks in YAML als Strings ("[[acme]]")

External References

Key Technical Decisions

  • Cron läuft lokal via launchd (LaunchAgent), nicht AWS-Lambda. Sessions liegen in ~/.claude/projects/, Lambda kann das nicht lesen. Lokal hat zusätzlich den Vorteil dass git log ohne API-Calls läuft.
  • cwd aus erster JSONL-Zeile als Repo-Identifier, nicht Ordner-Name-Decoding. Path-Encoding ist verlustbehaftet (Original-Pfade mit Dashes nicht reversibel). Erste Zeile enthält cwd der Session.
  • Scanner = einzelnes Python-Skript (keine Module-Pyramide). Layer-Code aus agents-platform/layers/agentic-common/ wird nicht wiederverwendet — der Scanner hat keine MCP/Bedrock-Abhängigkeiten, kein Bedarf für den Layer.
  • Kein Git-Commit aus dem Cron im MVP. Verhindert Konflikte mit Marvins Live-Edits + Daily-Briefing-Lambda-Commits. Frontmatter-Updates bleiben lokal als „dirty changes” sichtbar — Marvin committet selbst (oder nicht, irrelevant für Bases-Rendering).
  • Schreib-Strategie: in-place Frontmatter-Edit mit Idempotenz. Skript liest YAML-Block, mergt die 4 Activity-Felder, schreibt zurück. next_step und alle anderen Felder bleiben unangetastet.
  • Card-Layout via order: [status, next_step, file.mtime, formula.activity_label] — Bases gibt nur eine Reihenfolge vor, kein freies Layout. Eine formula baut „vor 3h” oder „gestern” aus last_activity_at.
  • code_repos: [] im Frontmatter pflegt Marvin manuell (3-5 aktive Projekte initial, einmaliger Setup-Aufwand). Wenn leer, scannt nur Vault-Ordner.

Open Questions

Resolved During Planning

  • Wo läuft der Cron? Lokal als launchd LaunchAgent. Begründung: Sessions sind lokal-only.
  • Was definiert „last_activity”? Maximum-Timestamp aus: letzter Git-Commit-Date (Vault-Ordner + code_repos) ODER letzte Session-Aktivität (letzte JSONL-Zeile-Timestamp). last_activity_source markiert welche Quelle gewann.
  • Was steht in last_activity_summary? Bei source=commit: erste Zeile der Commit-Msg. Bei source=claude-session: ai-title aus der ersten Zeile des JSONL.
  • Wie wird sessions_24h ermittelt? Anzahl unique sessionId in ~/.claude/projects/<encoded>/*.jsonl deren Last-Line-Timestamp innerhalb der letzten 24h liegt. Skript geht nur über Sessions, deren cwd in code_repos des Projekts steht.

Deferred to Implementation

  • Genaues .base-Card-Layout (welche Property prominent, Sortier-Direction): Im Unit 6 mit obsidian:obsidian-bases-Skill iterieren bis es visuell passt
  • Behandlung von Projekten ohne code_repos-Feld: Sind das alle aktiven? Erst migrieren (Unit 1) und im Cron-Skript einen Fallback (vault-only) bauen
  • YAML-Library-Wahl: ruamel.yaml (preserves comments + key-order) vs python-frontmatter (simpler, drops kommentare). Entscheidung in Unit 4 — direkt anhand realer _index.md testen
  • Performance bei vielen Sessions: Aktuell ~150 JSONL-Files in ~/.claude/projects/. Wenn der Scanner > 5 Sekunden braucht, Caching auf File-mtime einbauen — Detail für Unit 3
  • Wie reagiert der Scanner wenn ein Projekt verschoben/umbenannt wurde? Edge-Case in Unit 4 — neue Felder einfach setzen, kein Cleanup

High-Level Technical Design

Directional guidance, keine Implementation-Spec.

LaunchAgent (com.agenticventures.dashboard-scan.plist)
  ├─ trigger: StartCalendarInterval [06:00, 12:00, 18:00 Berlin]
  └─ ruft scripts/dashboard-scan.py auf
        │
        ├─ liest intern/firma/lokale-source-repos.md (Repo-Liste)
        │
        ├─ für jedes Projekt in intern/projekte/<slug>/_index.md:
        │     ├─ parsiert frontmatter (code_repos, current activity fields)
        │     ├─ ermittelt git_activity:
        │     │   ├─ git log -1 --format=%ct\|%s in <vault-projekt-ordner>
        │     │   └─ git log -1 --format=%ct\|%s in jedem code_repos[i]
        │     ├─ ermittelt claude_activity:
        │     │   ├─ für jeden code_repo: encode path → ~/.claude/projects/<encoded>/
        │     │   ├─ liest letzte Zeile jeder *.jsonl, sammelt timestamps
        │     │   ├─ liest erste Zeile für ai-title (Session-Name)
        │     │   └─ zählt sessions_24h
        │     ├─ wählt max-timestamp Quelle → last_activity_source
        │     └─ mergt in frontmatter, schreibt _index.md zurück
        │
        └─ schreibt scan-log nach intern/runs/dashboard-scan/<datum>.md

Obsidian (Marvin's iMac, offen)
  └─ rendert dashboard.base
        ├─ source: alle intern/projekte/**/_index.md mit type:project
        ├─ filter: status NICHT IN [archiviert, done]
        ├─ sort: last_activity_at DESC
        └─ view cards: Status-Badge + Title + next_step + "vor 3h via Session"

Implementation Units

flowchart TB
    U1[Unit 1: Schema-Erweiterung] --> U2[Unit 2: Repo-Mapping-Pflege]
    U1 --> U6[Unit 6: dashboard.base]
    U2 --> U3[Unit 3: Activity-Scanner]
    U3 --> U4[Unit 4: Frontmatter-Writer]
    U4 --> U5[Unit 5: LaunchAgent]
    U5 --> U7[Unit 7: Smoke-Test]
    U6 --> U7
  • Unit 1: Schema-Erweiterung um Activity-Felder + code_repos

Goal: Neue Frontmatter-Felder in _meta/schemas.md dokumentieren und in _meta/conventions.md referenzieren. Optional-Markierung, additiv.

Requirements: R6, R11

Dependencies: Keine

Files:

  • Modify: _meta/schemas.md (Sektion 5.5 Project erweitern um Activity-Felder + code_repos)
  • Modify: _meta/conventions.md falls dort Naming-Regeln stehen die referenziert werden müssen

Approach:

  • Neue Felder dokumentieren: last_activity_at (ISO-8601 datetime), last_activity_source (Enum: commit | claude-session | manual | none), last_activity_summary (string, max 100 Chars), sessions_24h (int), next_step (string, 1 Zeile), code_repos (Liste von Repo-Slugs aus intern/firma/lokale-source-repos.md)
  • Alle Activity-Felder als „auto-managed by dashboard-scan, nicht manuell editieren” markieren
  • next_step und code_repos als manuell-gepflegt markieren

Patterns to follow:

  • Bestehende Schema-Definitionen in _meta/schemas.md (Sektion 5.1-5.5)
  • Wikilink-as-string-Konvention

Test scenarios:

  • Test expectation: none — reine Doku-Änderung

Verification:

  • _meta/schemas.md enthält Sektion mit allen neuen Feldern + Auto-Managed-Hinweis

  • Beispiel-Frontmatter mit allen neuen Feldern ist im Schema-Block sichtbar

  • Unit 2: code_repos-Mapping in aktiven Projekt-_index.md pflegen

Goal: Manuelle Migration: in jedem intern/projekte/<slug>/_index.md mit status: active das Feld code_repos: [...] hinzufügen. Für Projekte ohne Code-Repo bleibt das Array leer.

Requirements: R11

Dependencies: Unit 1

Files:

  • Modify: alle intern/projekte/*/_index.md mit status: active (etwa 8-12 Stück)

Approach:

  • Pro Projekt entscheiden welche Repos relevant sind. Referenz: lokale-source-repos.md
  • Beispiele: bas-twin: ["bas-twin"], mcp-pipeline-aws: ["mcps", "agents-platform"], daily-briefing: ["agents-platform"], av-website: ["agentic-ventures-website"]
  • Falls Projekt nur im Vault lebt (Doku-Projekte): code_repos: []

Patterns to follow:

  • Bestehende related:-Listen mit Wikilinks als Vorbild für Listen-Format

Test scenarios:

  • Test expectation: none — manuelle Daten-Pflege

Verification:

  • Jedes aktive Projekt-_index.md hat code_repos: Feld (auch wenn [])

  • Repo-Namen matchen die Ordner in ~/source/ (kein Tippfehler)

  • Unit 3: Activity-Scanner (Python-Skript)

Goal: Pures Python-Skript das pro Projekt die Activity-Daten ermittelt. Liest, schreibt nicht — Output ist ein Dictionary pro Projekt.

Requirements: R8, R9

Dependencies: Unit 2 (braucht code_repos als Input)

Files:

  • Create: scripts/dashboard-scan/scan.py (im Vault-Repo unter scripts/dashboard-scan/)
  • Create: scripts/dashboard-scan/README.md (kurze Doku: was, wie aufrufen, dependencies)
  • Create: scripts/dashboard-scan/requirements.txt (ruamel.yaml falls gewählt, sonst stdlib only)
  • Test: scripts/dashboard-scan/test_scan.py

Approach:

  • Function scan_project(project_path: Path) -> dict liest Frontmatter, ermittelt Activity, gibt Dict mit den 4 Activity-Feldern zurück
  • Function git_last_activity(repo_path: Path) -> tuple[datetime, str] ruft git log -1 --format=%cI|%s via subprocess.run, parst Output
  • Function claude_sessions_for_repo(repo_abs_path: Path) -> list[dict] encodiert Pfad mit replace("/", "-"), sammelt JSONL-Files in ~/.claude/projects/<encoded>/
  • Pro JSONL-File: letzte Zeile via tail-equivalent (read last N bytes) → JSON-Parse → Timestamp. Erste Zeile (falls ai-title) → Session-Name
  • Aggregation: max-timestamp gewinnt, last_activity_source markiert Herkunft. sessions_24h = count(sessions where last_ts > now - 24h)
  • Kein Schreibzugriff in dieser Unit — reines Lese-Modul, gibt Daten zurück. Schreibzugriff in Unit 4

Patterns to follow:

  • daaain/claude-code-log Scan-Loop als Vorbild
  • Defensives Parsing: skip-on-error pro Session (eine kaputte JSONL bricht nicht den ganzen Scan)

Test scenarios:

  • Happy path: Projekt mit code_repos: ["bas-twin"] + existierende Sessions liefert korrekte last_activity_at + source + summary
  • Happy path: Projekt mit code_repos: [] und kein Vault-Ordner-Commit liefert last_activity_source: "none"
  • Edge case: Projekt-Vault-Ordner ohne Commits (frisch angelegt) liefert keine git-Aktivität, fällt auf Sessions zurück
  • Edge case: Kaputte JSONL-Datei (truncated) wird übersprungen, Skript läuft weiter
  • Edge case: ~/.claude/projects/<encoded>/ existiert nicht (nie damit gearbeitet) → leere Session-Liste, kein Crash
  • Error path: git log schlägt fehl (nicht-Git-Ordner) → catch, return None, kein Crash
  • Integration: Scanner über alle aktiven Projekte läuft < 10s auf Marvins iMac

Verification:

  • python scripts/dashboard-scan/scan.py --dry-run printet pro aktivem Projekt ein dict mit 4 korrekten Activity-Feldern

  • Tests: pytest scripts/dashboard-scan/test_scan.py grün

  • Unit 4: Frontmatter-Writer (in-place Merge)

Goal: Schreibt die 4 Activity-Felder zurück in _index.md, lässt alle anderen Felder + Markdown-Body unangetastet.

Requirements: R6, R7

Dependencies: Unit 3

Files:

  • Modify: scripts/dashboard-scan/scan.py (ergänzt um write_frontmatter(path, fields))
  • Test: scripts/dashboard-scan/test_scan.py (Tests für Writer)

Approach:

  • YAML-Library-Wahl in Implementation (siehe Open Question): ruamel.yaml bevorzugt wegen Erhalt von Kommentaren + Key-Order, fallback python-frontmatter falls Setup-Hürde
  • Read-Merge-Write: Frontmatter parsen, 4 Activity-Felder setzen/überschreiben, keine anderen Felder anfassen (auch nicht next_step)
  • Wenn Feld vorher nicht existierte: hinzufügen am Ende des Frontmatter-Blocks
  • Atomic write: schreibe in Temp-File, dann os.replace — verhindert Half-Writes wenn Obsidian gerade liest
  • --dry-run Flag zeigt Diff statt zu schreiben

Patterns to follow:

  • Atomic-write Pattern (tempfile.NamedTemporaryFile + os.replace)

Test scenarios:

  • Happy path: Bestehende _index.md mit minimalem Frontmatter wird um Activity-Felder ergänzt, Body unverändert
  • Happy path: _index.md mit bestehenden Activity-Feldern wird aktualisiert (Werte ersetzt, nicht dupliziert)
  • Edge case: next_step ist gesetzt — Writer fasst es nicht an
  • Edge case: Frontmatter mit Wikilink-Strings (customer: "[[becker]]") bleibt nach Roundtrip intakt (Quotes erhalten)
  • Edge case: Frontmatter mit YAML-Listen mehrzeilig (team: \n - "[[marvin]]") bleibt erhalten
  • Edge case: File hat Kommentare im Frontmatter → bleiben erhalten (ruamel) oder dokumentierte Lossy-Behavior (frontmatter)
  • Error path: File existiert nicht → loggen, skip, kein Crash
  • Error path: Frontmatter-Block kaputt (kein schliessendes ---) → loggen, skip
  • Integration: Komplett-Run über alle aktiven Projekte, git diff zeigt nur Activity-Field-Änderungen

Verification:

  • Nach python scripts/dashboard-scan/scan.py zeigt git diff intern/projekte/ ausschliesslich Änderungen in den 4 Activity-Feldern

  • Markdown-Body von 5 Stichproben-Projekten ist Byte-gleich vor und nach Scan

  • Unit 5: LaunchAgent + Wrapper-Skript

Goal: macOS LaunchAgent das den Scanner 3x täglich startet, mit Lockfile, sauberer PATH, Logs in Vault.

Requirements: R8

Dependencies: Unit 4

Files:

  • Create: scripts/dashboard-scan/run.sh (Wrapper mit Lockfile + Env-Setup + Logging)
  • Create: scripts/dashboard-scan/com.agenticventures.dashboard-scan.plist (Template, wird in ~/Library/LaunchAgents/ symlinked oder kopiert)
  • Create: scripts/dashboard-scan/install.sh (One-shot Installer: copy/symlink + launchctl load)
  • Create: scripts/dashboard-scan/uninstall.sh (launchctl unload + cleanup)
  • Modify: intern/firma/stack.md (LaunchAgent-Eintrag dokumentieren, falls Stack-Inventory dort)

Approach:

  • plist: StartCalendarInterval mit drei Slots (06:00, 12:00, 18:00). StandardOutPath und StandardErrorPath zeigen auf ~/Library/Logs/dashboard-scan.log
  • RunAtLoad: false (kein Spam beim Login)
  • EnvironmentVariables: setze PATH=/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin, HOME
  • Wrapper run.sh:
    • Lockfile via flock oder mkdir-Trick — wenn schon läuft, exit 0
    • cd $VAULT_PATH
    • python3 scripts/dashboard-scan/scan.py >> log 2>&1
    • Lock release
  • Installer: plutil -lint vor Load, Pfad-Check, idempotent (uninstall vorher)

Patterns to follow:

  • Bas-Man’s launchd-Best-Practices (Linked Source)
  • Existierende ~/Library/LaunchAgents/-Konventionen (Reverse-FQDN-Naming)

Test scenarios:

  • Happy path: install.sh legt plist an, launchctl list | grep dashboard-scan zeigt geladenen Agent
  • Happy path: Manueller Trigger via launchctl start com.agenticventures.dashboard-scan läuft erfolgreich, Log zeigt Scan-Output
  • Edge case: Zweiter Trigger während erster läuft (Lock-File greift) → exit ohne Doppellauf
  • Edge case: python3 nicht im PATH → Wrapper loggt Fehler verständlich (nicht silent fail)
  • Error path: Vault-Repo hat uncommitted Changes — Skript läuft trotzdem (es committet nichts)
  • Integration: Nach Install läuft der Scan zum nächsten Slot automatisch ohne manuelles Eingreifen
  • Uninstall: uninstall.sh entfernt plist + entlädt Agent, launchctl list zeigt ihn nicht mehr

Verification:

  • plutil -lint scripts/dashboard-scan/com.agenticventures.dashboard-scan.plist → OK

  • Nach Manual-Trigger: intern/projekte/*/index.md haben aktualisierte Activity-Felder, ~/Library/Logs/dashboard-scan.log zeigt sauberen Run

  • Unit 6: dashboard.base (Obsidian Bases Card-View)

Goal: Eine .base-Datei im Vault die das Karten-Grid rendert.

Requirements: R1, R2, R3, R4, R5

Dependencies: Unit 1 (Schema), parallel zu Unit 2-5 möglich

Files:

  • Create: dashboard.base im Vault-Root (oder intern/dashboard.base — Decision in Implementation, je nach Obsidian-Konvention)
  • Optional: intern/_context.md ergänzen mit Hinweis auf die Base

Approach:

  • Vor dem Bau: den Skill obsidian:obsidian-bases aufrufen, der hat die Card-Layout-Feinheiten
  • Top-Level: filters, formulas, views
  • Filter: and: ['type == "project"', 'status != "archiviert"', 'status != "done"']
  • Formula activity_label: konvertiert last_activity_at in „vor X Stunden/Tagen/manual”
  • View type: cards, name: "Aktive Projekte", sort: [{property: last_activity_at, direction: DESC}]
  • order: [status, title, next_step, formula.activity_label, sessions_24h, customer]
  • Tests visuell in Obsidian — keine Unit-Tests möglich

Patterns to follow:

  • Offizielle Obsidian-Bases-Beispiele
  • Lokaler Skill obsidian:obsidian-bases

Test scenarios:

  • Test expectation: visual — keine Code-Tests. Aber Akzeptanzkriterien:
  • Visual: Bei Öffnen rendern alle aktiven Projekte als Karten
  • Visual: Archivierte und done-Projekte erscheinen nicht
  • Visual: Karten sind nach Activity sortiert (frischestes oben)
  • Visual: Klick auf eine Karte öffnet _index.md
  • Visual: next_step-Zeile ist prominent lesbar

Verification:

  • Marvin öffnet die Base in Obsidian → Karten sind sichtbar, sortiert, gefiltert

  • Manuelles Setzen status: done in einer Projekt-_index.md lässt Karte verschwinden ohne Reload

  • Unit 7: End-to-End Smoke-Test + Doku

Goal: Verifikation dass alle Units zusammen funktionieren. Doku in Vault.

Requirements: S1, S2, S3, S4

Dependencies: Unit 5, Unit 6

Files:

  • Modify: intern/projekte/projekt-dashboard/_index.md (status → deployed, Dokumentation der Setup-Schritte)
  • Create: intern/projekte/projekt-dashboard/runbook.md (Wie Re-Run? Wie debuggen? Wie deaktivieren?)
  • Modify: intern/wissen/prozesse/ ggf. Eintrag für „Pattern: lokaler Cron schreibt Vault-Frontmatter” falls wiederverwendbar
  • Modify: CLAUDE.md Routing-Tabelle: Zeile „Projekt-Dashboard” ergänzen

Approach:

  • Trigger einen vollen Scan manuell, prüfe Frontmatter in 5 Stichproben
  • Setze einen Projekt-Status auf archiviert, prüfe dass Karte verschwindet
  • Editiere next_step in einem Projekt, prüfe dass Karte das anzeigt
  • 24h-Wartetest: ein automatischer Trigger läuft, Logs sehen sauber aus

Test scenarios:

  • Happy path: Voller Scan + Bases-View zeigt frische Daten
  • Integration: Marvin pflegt next_step per Hand — bleibt erhalten über mehrere Scans
  • Integration: Neue Session in Claude Code → nächster Scan registriert sie im sessions_24h-Counter
  • Edge case: Marvin verschiebt ein Projekt-Ordner — Scanner bricht nicht, neues Projekt erscheint, altes verschwindet (oder bleibt mit veralteten Daten — dokumentieren)

Verification:

  • S1 erfüllt: morgendliches Öffnen der Base zeigt sofort welche Projekte gestern Activity hatten
  • S2 erfüllt: Drift zwischen last_activity_at und realem letzten Commit < 12h
  • S3 erfüllt: Marvin pflegt nur next_step (gemessen über 1 Woche)
  • S4 erfüllt: Karte zeigt „letzte Session: ‘Set up AWS MCP and organize multi-customer accounts’ (vor 3h)” — direkt klickbare Antwort auf „welche Session war das?”

System-Wide Impact

  • Interaction graph: Frontmatter-Updates ändern alle aktiven Projekt-_index.md. Daily-Briefing-Lambda liest aktuell operations/action-items.md via GitHub-API — wird nicht beeinflusst. Andere Vault-Konsumenten (manuelles Lesen, andere Skills) sehen lediglich zusätzliche Felder.
  • Error propagation: Scanner-Fehler dürfen nicht die _index.md-Dateien beschädigen. Atomic Writes + Skip-on-Error pro Projekt sind Pflicht. Wenn Scanner ganz crasht, bleibt der Vault-State unverändert.
  • State lifecycle risks: Konflikt-Risiko zwischen Cron-Writes und Marvin-Live-Edits in Obsidian. Mitigation: kein Git-Commit aus Cron, kein File-Lock — letzter Writer gewinnt. Obsidian-File-Watch greift, Marvin sieht updates live. Wenn Marvin gerade tippt und Scanner schreibt: Obsidian merged in den meisten Fällen sauber, im Worst-Case sieht Marvin einen Sync-Konflikt-Dialog (selten bei 3 Scans/Tag).
  • API surface parity: Keine externen API-Konsumenten. Vault-Schema bleibt rückwärtskompatibel (additive Felder).
  • Integration coverage: Cross-Layer-Test: Scanner schreibt → Obsidian-Bases liest → Marvin sieht. Nur via Manual-Test verifizierbar.
  • Unchanged invariants: Bestehende Frontmatter-Felder (id, type, status, customer, etc.) werden nie überschrieben. Markdown-Body wird nie angefasst. Daily-Briefing-Cron + Beleg-Pipeline laufen unverändert weiter.

Risks & Dependencies

RiskMitigation
Scanner beschädigt _index.md-Dateien (kaputtes YAML)Atomic Write + Roundtrip-Test im Unit-Test-Setup. Vor erstem Live-Run: Trockenlauf gegen Backup-Kopie des Vaults
Konflikt mit Marvins gleichzeitigen EditsCron läuft 3x täglich zu festen Zeiten (6/12/18 Uhr) — Marvin kann manuell pausieren via launchctl unload. Plus: kein Git-Commit aus Cron, lokale Diffs sichtbar
Obsidian Bases hat noch Rough-Edges (relativ neue Feature)Skill obsidian:obsidian-bases beim Bauen nutzen + bei Bugs einfache YAML-Anpassung im .base-File
code_repos-Pflege wird vergessen → veraltete KartenScanner loggt explizit „Projekt X hat keine code_repos und keinen Vault-Commit seit 30 Tagen” als Hinweis in Telegram falls gewünscht (Phase 2)
LaunchAgent läuft nicht weil iMac in SleepStartCalendarInterval queued bei Wake — built-in Behavior, keine extra Action
Sessions-Files bekommen ein neues Format (Anthropic-Update)Scanner defensive parsing, skip-on-error pro File. Bei breaking change: in Unit 3 Stelle das Parsing-Tolerance einbauen

Documentation / Operational Notes

  • Routing-Eintrag in CLAUDE.md: „Projekt-Dashboard / Cron-Scan” → diese Plan-Datei + Runbook
  • Schema-Doku in _meta/schemas.md muss Activity-Felder als „auto-managed” markieren (Unit 1)
  • Runbook in intern/projekte/projekt-dashboard/runbook.md: wie pausieren, wie debuggen, wie deinstallieren
  • Logs in ~/Library/Logs/dashboard-scan.log — bei Bedarf rotation via newsyslog (Phase 2)
  • Wenn Phase 2 (Telegram-Stream) kommt: agents-platform Lambda könnte Telegram-Pushes lesen und ans Vault-File koppeln (Cross-Source-Aggregation) — Open Design für später

Sources & References