# Dangling Target Cases - Übersicht ## Welche Fälle werden erkannt? Chain Inspector v0.2+ erkennt zwei Arten von `dangling_target` Findings: ### 1. `dangling_target` (Severity: ERROR) **Wann wird es erkannt?** - Ein **outgoing Edge** von der aktuellen Section verweist auf eine **Datei, die nicht existiert** - Die Datei wird über `app.metadataCache.getFirstLinkpathDest()` aufgelöst - Wenn die Auflösung `null` zurückgibt → `dangling_target` Finding **Welche Edges werden geprüft?** - Alle **section-scoped** Edges aus der aktuellen Section (nicht Note-scoped, nicht Candidates) - Edges aus expliziten Edge-Callouts: `> [!edge] \n> [[TargetFile]]` - Edges aus Semantic Mapping Blocks **Beispiele:** ``` ## Meine Section > [!edge] causes > [[MissingNote]] ← dangling_target (ERROR) > [!abstract] 🕸️ Semantic Mapping >> [!edge] influences >> [[AnotherMissingNote]] ← dangling_target (ERROR) ``` **Was wird NICHT geprüft?** - ❌ Note-scoped Edges (aus "## Note-Verbindungen" Zone) - ❌ Candidate Edges (aus "## Kandidaten" Zone) - ❌ Incoming Edges (nur outgoing Edges werden geprüft) ### 2. `dangling_target_heading` (Severity: WARN) **Wann wird es erkannt?** - Ein **outgoing Edge** verweist auf eine **existierende Datei**, aber mit einem **Heading, das nicht existiert** - Die Datei existiert (wird erfolgreich aufgelöst) - Das Heading wird in `metadataCache.getFileCache()` geprüft - Wenn das Heading nicht in der Liste der Headings gefunden wird → `dangling_target_heading` Finding **Beispiele:** ``` ## Meine Section > [!edge] references > [[ExistingNote#MissingHeading]] ← dangling_target_heading (WARN) > [!edge] part_of > [[AnotherNote#NonExistentSection]] ← dangling_target_heading (WARN) ``` **Was passiert wenn File Cache nicht verfügbar ist?** - Wenn `getFileCache()` `null` zurückgibt (z.B. Datei noch nicht indexiert) - → **Kein Finding** (wird übersprungen, da Cache später aktualisiert wird) ## Welche Fälle sind behebbar? ### ✅ Behebbar via Fix Actions: 1. **`dangling_target` (ERROR)** - ✅ **Action 1: Create Missing Note** - Erstellt neue Note (skeleton oder mit Profile/Wizard) - Verwendet Settings: `fixActions.createMissingNote.*` - ✅ **Action 2: Retarget Link** - Zeigt Entity Picker Modal - Ersetzt Link im Editor zu existierender Note - Behält Heading bei wenn vorhanden 2. **`dangling_target_heading` (WARN)** - ✅ **Action 1: Create Missing Heading** - Erstellt Heading am Ende der Target-Datei - Verwendet Settings: `fixActions.createMissingHeading.level` - ✅ **Action 2: Retarget to Existing Heading** - Zeigt Heading-Picker (aus File Cache) - Ersetzt Link im Editor zu existierendem Heading 3. **`only_candidates` (INFO)** - ✅ **Action: Promote Candidate Edge** - Befördert Candidate-Edge zu explizitem Edge - Fügt Edge zu aktueller Section hinzu - Verwendet Settings: `fixActions.promoteCandidate.keepOriginal` ### ❌ NICHT behebbar (keine Fix Actions): - `missing_edges` - Section hat keine Edges (nur Info) - `one_sided_connectivity` - Nur incoming ODER nur outgoing Edges (nur Info) - `no_causal_roles` - Keine causal-ish Rollen (nur Info) ## Technische Details ### Edge-Filterung für `dangling_target` Check ```typescript // sectionEdges = alle Edges aus aktueller Section const sectionEdges = currentEdges.filter((edge) => { // Nur section-scoped Edges (nicht note-scoped, nicht candidates) if (edge.scope === "candidate") return false; if (edge.scope === "note") return false; // Nur Edges aus aktueller Section return "sectionHeading" in edge.source ? edge.source.sectionHeading === context.heading && edge.source.file === context.file : false; }); ``` ### Datei-Auflösung ```typescript const resolvedFile = app.metadataCache.getFirstLinkpathDest( normalizeLinkTarget(targetFile), context.file ); if (!resolvedFile) { // → dangling_target (ERROR) } ``` ### Heading-Check ```typescript if (targetHeading !== null) { const targetContent = app.metadataCache.getFileCache(resolvedFile); if (targetContent) { const headings = targetContent.headings || []; const headingExists = headings.some( (h) => h.heading === targetHeading ); if (!headingExists) { // → dangling_target_heading (WARN) } } } ``` ## Bekannte Einschränkungen 1. **Trash Detection:** Dateien im `.trash` Ordner werden als "nicht existierend" erkannt (korrektes Verhalten) 2. **File Cache:** Wenn `getFileCache()` nicht verfügbar ist, wird Heading-Check übersprungen (akzeptabel, da Cache später aktualisiert wird) 3. **Case Sensitivity:** Heading-Matching ist case-sensitive (abhängig von Obsidian-Konfiguration) 4. **Note-scoped Edges:** Werden nicht auf `dangling_target` geprüft (nur section-scoped Edges) 5. **Candidate Edges:** Werden nicht auf `dangling_target` geprüft (nur explizite Edges) ## Zusammenfassung | Finding | Severity | Geprüft für | Behebbar | Actions | |---------|----------|-------------|----------|---------| | `dangling_target` | ERROR | Outgoing section-scoped Edges | ✅ | Create Note, Retarget Link | | `dangling_target_heading` | WARN | Outgoing section-scoped Edges mit Heading | ✅ | Create Heading, Retarget to Heading | | `only_candidates` | INFO | Sections mit nur Candidate-Edges | ✅ | Promote Candidate | | `missing_edges` | INFO | Sections ohne Edges | ❌ | - | | `one_sided_connectivity` | INFO | Sections mit nur incoming/outgoing | ❌ | - | | `no_causal_roles` | INFO | Sections ohne causal roles | ❌ | - |