Write stable block-id links and always start the interview wizard.
Some checks failed
Node.js build / build (20.x) (push) Failing after 8s
Node.js build / build (22.x) (push) Failing after 9s

New notes and workbench edges now emit [[Note#Heading ^block]] or [[#^id]] so vaults stay ingestible without mass relinks. Create-from-profile always opens the wizard; a command can restart it on the current note.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Lars 2026-08-13 17:25:09 +02:00
parent c40a89096f
commit 56ae6a2407
29 changed files with 1448 additions and 274 deletions

View File

@ -1,187 +1,110 @@
# Mindnet Causal Assistant - Dokumentations-Index
# Mindnet Causal Assistant Dokumentations-Index
> **Version:** 1.0.0
> **Stand:** 2025-01-XX
> **Vollständige Übersicht:** Alle Dokumentations-Dateien und deren Inhalte
> **Stand:** Januar 2026
> **Plugin:** Mindnet Causal Assistant (Obsidian)
---
## Dokumentations-Struktur
## Übersicht
### Hauptdokumentation (nach Zielgruppe)
Die Dokumentation ist in **zwei Perspektiven** organisiert:
1. **[01_Benutzerhandbuch.md](./01_Benutzerhandbuch.md)** - Endnutzer
2. **[02_Administratorhandbuch.md](./02_Administratorhandbuch.md)** - Administratoren
3. **[03_Entwicklerhandbuch.md](./03_Entwicklerhandbuch.md)** - Entwickler
4. **[04_Architektur.md](./04_Architektur.md)** - Architekten
5. **[05_Installation_Deployment.md](./05_Installation_Deployment.md)** - Installation
### Spezialisierte Referenzen
6. **[06_Konfigurationsdateien_Referenz.md](./06_Konfigurationsdateien_Referenz.md)** - Config-Dateien Format & Aufbau
7. **[Interview_Config_Guide.md](./Interview_Config_Guide.md)** - Vollständige Anleitung für `interview_config.yaml` (inkl. WP-26 Features, GenAI-freundlich)
8. **[07_Event_Handler_Commands.md](./07_Event_Handler_Commands.md)** - Event Handler & Commands
### WP-26 Integration (Section Types & Intra-Note-Edges)
9. **[06_LH_WP26_Plugin_Integration.md](./06_LH_WP26_Plugin_Integration.md)** - Lastenheft für WP-26 Plugin-Integration (vollständige Anforderungen)
10. **[WP26_Plugin_Interface_Specification.md](./WP26_Plugin_Interface_Specification.md)** - Vollständige Schnittstellenspezifikation für Plugin-Entwicklung
11. **[WP26_Implementation_Checklist.md](./WP26_Implementation_Checklist.md)** - Implementierungs-Checkliste mit Tasks und Phasen
### Chain Inspector Reports
- [CHAIN_INSPECTOR_V0_REPORT.md](./CHAIN_INSPECTOR_V0_REPORT.md) - v0.0 Implementierung
- [CHAIN_INSPECTOR_V02_REPORT.md](./CHAIN_INSPECTOR_V02_REPORT.md) - v0.2 Features
- [CHAIN_INSPECTOR_V03_REPORT.md](./CHAIN_INSPECTOR_V03_REPORT.md) - v0.3 Features
- [CHAIN_INSPECTOR_V04_REPORT.md](./CHAIN_INSPECTOR_V04_REPORT.md) - v0.4 Template Matching
- [CHAIN_INSPECTOR_V042_REPORT.md](./CHAIN_INSPECTOR_V042_REPORT.md) - v0.4.2 Updates
### Konzepte & Details
- [02_causal_chain_retrieving.md](./02_causal_chain_retrieving.md) - Kausale Ketten-Retrieval
- [DANGLING_TARGET_CASES.md](./DANGLING_TARGET_CASES.md) - Dangling Target Findings
### Legacy-Dokumentation
- [readme.md](./readme.md) - MVP 1.0 Quickstart
- [Handbuch.md](./Handbuch.md) - MVP 1.0 Handbuch
- [TESTING_WITH_REAL_VAULT.md](./TESTING_WITH_REAL_VAULT.md) - Testing mit echtem Vault
1. **Nach Modul** technische Inhalte nach Komponenten (Analysis, Interview, Mapping, usw.)
2. **Nach Rolle** gezielte Einstiege für Nutzer, Administratoren, Entwickler, Architekten, Projektleitung
---
## Vollständigkeits-Checkliste
## Einstieg nach Rolle
### ✅ Module & Komponenten
- [x] **Analysis** (`src/analysis/`) - Chain Inspector, Template Matching, Graph Index, Section Context, Severity Policy
- [x] **Commands** (`src/commands/`) - Inspect Chains, Fix Findings
- [x] **Dictionary** (`src/dictionary/`) - Chain Roles Loader, Chain Templates Loader, Config Path Manager, Dictionary Loader, Parsers, Types
- [x] **Entity Picker** (`src/entityPicker/`) - Note Index, Folder Tree, Filters, Wikilink
- [x] **Export** (`src/export/`) - Graph Export
- [x] **Graph** (`src/graph/`) - Graph Builder, Graph Index, Render Chain Report, Resolve Target, Traverse
- [x] **Interview** (`src/interview/`) - Config Loader, Parser, Wizard State, Loop State, Renderer, Write Frontmatter, Section Key Resolver, Extract Target, Slugify
- [x] **Lint** (`src/lint/`) - Lint Engine, Rules (Hub Has Causality, Missing Target, Unknown Edge)
- [x] **Mapping** (`src/mapping/`) - Semantic Mapping Builder, Mapping Extractor, Mapping Builder, Edge Type Selector, Update Mapping Blocks, Section Parser, Worklist Builder, Graph Schema, Schema Helper, Folder Helpers
- [x] **Parser** (`src/parser/`) - Parse Edges From Callouts, Parse Frontmatter, Parse Rel Links
- [x] **Schema** (`src/schema/`) - Graph Schema Loader
- [x] **UI** (`src/ui/`) - Alle Modals (Interview Wizard, Profile Selection, Edge Type Chooser, Entity Picker, Adopt Note, Folder Tree, Link Prompt, Inline Edge Type, Confirm Overwrite), Settings Tab, Markdown Toolbar, Mindnet View
- [x] **Unresolved Link** (`src/unresolvedLink/`) - Handler, Link Helpers, Adopt Helpers
- [x] **Vocabulary** (`src/vocab/`) - Vocabulary, Vocabulary Loader, Parse Edge Vocabulary
### ✅ Funktionen & Features
- [x] **Note-Erstellung** - Mit Profilen, Frontmatter-Generierung
- [x] **Interview-Wizard** - Steps, Loops, Nested Loops, Section-basierte Ausgabe
- [x] **Semantic Mapping** - Builder, Extractor, Edge Type Assignment
- [x] **Chain Inspector** - Template Matching, Findings Generation
- [x] **Fix Findings** - Automatische Behebung von Findings
- [x] **Unresolved Link Handling** - Reading View, Editor, Note Adoption
- [x] **Edge-Type-Änderung** - Kontext-Erkennung, Edge Type Selector
- [x] **Graph Export** - JSON-Export
- [x] **Linting** - Lint Engine mit Regeln
- [x] **Live-Reload** - Automatisches Neuladen von Config-Dateien
### ✅ Commands
- [x] `mindnet-reload-edge-vocabulary` - Edge Vocabulary neu laden
- [x] `mindnet-validate-current-note` - Note validieren (Lint)
- [x] `mindnet-export-graph` - Graph exportieren
- [x] `mindnet-show-chains-from-current-note` - Ketten von aktueller Note zeigen
- [x] `mindnet-create-note-from-profile` - Note mit Profil erstellen
- [x] `mindnet-change-edge-type` - Edge-Type ändern
- [x] `mindnet-debug-chain-roles` - Chain Roles Debug-Info
- [x] `mindnet-debug-chain-templates` - Chain Templates Debug-Info
- [x] `mindnet-fix-findings` - Findings beheben
- [x] `mindnet-inspect-chains` - Chains analysieren
- [x] `mindnet-build-semantic-mappings` - Semantic Mapping Blöcke bauen
### ✅ Event Handler
- [x] **Vault Modify Event** - Live-Reload von Config-Dateien
- [x] **Vault Create Event** - Note Adoption
- [x] **Markdown Post Processor** - Unresolved Link Handling (Reading View)
- [x] **DOM Click Event** - Unresolved Link Handling (Editor)
### ✅ Settings
- [x] **Pfad-Settings** (6) - Alle Config-Datei-Pfade
- [x] **Graph & Chain Settings** (4) - maxHops, strictMode, showCanonicalHints, chainDirection
- [x] **Interview Settings** (1) - autoStartInterviewOnCreate
- [x] **Unresolved Link Settings** (6) - interceptUnresolvedLinkClicks, autoStartOnUnresolvedClick, bypassModifier, editorFollowModifier, waitForFirstModifyAfterCreate, waitForModifyTimeoutMs, debugLogging
- [x] **Note Adoption Settings** (4) - adoptNewNotesInEditor, adoptMaxChars, adoptConfirmMode, highConfidenceWindowMs
- [x] **Semantic Mapping Settings** (6) - mappingWrapperCalloutType, mappingWrapperTitle, mappingWrapperFolded, defaultEdgeType, unassignedHandling, allowOverwriteExistingMappings, defaultNotesFolder
- [x] **Inline Micro Edge Suggester Settings** (3) - inlineMicroEnabled, inlineMaxAlternatives, inlineCancelBehavior
- [x] **Export Settings** (1) - exportPath
- [x] **Chain Inspector Settings** (3) - chainInspectorIncludeCandidates, chainInspectorMaxTemplateMatches, templateMatchingProfile
- [x] **Fix Actions Settings** (5) - createMissingNote (mode, defaultTypeStrategy, includeZones), createMissingHeading (level), promoteCandidate (keepOriginal)
### ✅ Konfigurationsdateien
- [x] **edge_vocabulary.md** - Format, Parsing-Regeln, Beispiel
- [x] **graph_schema.md** - Format, Parsing-Regeln, Beispiel
- [x] **interview_config.yaml** - Format, Felder, Beispiel (Profile, Steps, Loops) - **Siehe auch:** [Interview_Config_Guide.md](./Interview_Config_Guide.md) für vollständige Anleitung
- [x] **chain_roles.yaml** - Format, Felder, Beispiel (Roles, Edge Types)
- [x] **chain_templates.yaml** - Format, Felder, Beispiel (Templates, Slots, Links, Defaults, Profiles)
- [x] **analysis_policies.yaml** - Geplante Struktur (noch nicht vollständig implementiert)
### ✅ Wirkungsweise
- [x] **Live-Reload** - Debounced (200ms), Last-Known-Good Fallback
- [x] **Config-Loading** - YAML/Markdown Parsing, Error Handling
- [x] **Template Matching** - Slot-basiertes Matching, Link Constraints
- [x] **Findings Generation** - Gap-Heuristiken, Severity-Policy
- [x] **Note Adoption** - Confidence-Evaluation, Adoption-Flow
- [x] **Unresolved Link Handling** - Reading View vs Editor, Modifier-Keys
- [x] **Semantic Mapping** - Section-Parsing, Link-Extraktion, Edge-Type-Assignment
| Rolle | Primäre Dokumente | Kurzbeschreibung |
|-------|-------------------|------------------|
| **Nutzer** | [13_Erfassung_Causal_Ketten](13_Erfassung_Causal_Ketten.md), [01_Benutzerhandbuch](01_Benutzerhandbuch.md), [07_Event_Handler_Commands](07_Event_Handler_Commands.md) | Erfassung & Ketten, Installation, Commands, Troubleshooting |
| **Administrator** | [02_Administratorhandbuch](02_Administratorhandbuch.md), [06_Konfigurationsdateien_Referenz](06_Konfigurationsdateien_Referenz.md), [Interview_Config_Guide](Interview_Config_Guide.md) | Konfiguration, Pfade, Live-Reload, Wartung |
| **Entwickler** | [03_Entwicklerhandbuch](03_Entwicklerhandbuch.md), [04_Architektur](04_Architektur.md), [Module](modules/README.md) | Code-Struktur, Testing, Build, Erweiterungen |
| **Architekt** | [04_Architektur](04_Architektur.md), [02_concepts/03_chain_identification_and_matching](02_concepts/03_chain_identification_and_matching.md) | System-Design, Datenfluss, Konzepte |
| **Projektleitung** | [04_Architektur](04_Architektur.md), [README](../README.md), [STATUS_DOD](../STATUS_DOD.md) | Überblick, Scope, Definition of Done |
| **Systemadmin** | [05_Installation_Deployment](05_Installation_Deployment.md), [02_Administratorhandbuch](02_Administratorhandbuch.md) | Installation, Deployment, Umgebung |
---
## Dokumentations-Abdeckung
## Einstieg nach Modul
### Vollständig dokumentiert
**Alle Module** - Alle 14 Hauptmodule sind dokumentiert
**Alle Commands** - Alle 11 Commands sind dokumentiert
**Alle Event Handler** - Alle 4 Event Handler sind dokumentiert
**Alle Settings** - Alle 40+ Settings sind dokumentiert
**Alle Config-Dateien** - Alle 6 Config-Dateien sind dokumentiert
**Wirkungsweise** - Alle Hauptfunktionen sind beschrieben
**Konfiguration** - Format und Aufbau aller Config-Dateien sind dokumentiert
### Teilweise dokumentiert
⚠️ **Analysis Policies** - Erwähnt, aber noch nicht vollständig implementiert
⚠️ **Einige UI-Komponenten** - Basis-Funktionalität dokumentiert, Details könnten erweitert werden
### Nicht dokumentiert (nicht vorhanden oder nicht relevant)
- **Tests** - Test-Dateien sind nicht Teil der Benutzer-Dokumentation
- **Mocks** - Mock-Dateien sind nicht Teil der Benutzer-Dokumentation
- **Build-Scripts** - Build-Scripts sind in Entwicklerhandbuch dokumentiert
| Modul | Pfad | Beschreibung |
|-------|------|--------------|
| **Analysis** | [modules/analysis](modules/analysis.md) | Chain Inspector, Template Matching, Scoring, Findings |
| **Dictionary** | [modules/dictionary](modules/dictionary.md) | Config-Loader, Chain Roles, Chain Templates, Dictionary |
| **Interview** | [modules/interview](modules/interview.md) | Wizard, Loop State, Renderer, Section Key Resolver |
| **Mapping** | [modules/mapping](modules/mapping.md) | Semantic Mapping, Edge Type Selector, Update Mapping Blocks |
| **Parser** | [modules/parser](modules/parser.md) | Edges aus Callouts, Frontmatter, Links |
| **Graph** | [modules/graph](modules/graph.md) | Graph Builder, Traversal, Resolution, Export |
| **Vocabulary** | [modules/vocabulary](modules/vocabulary.md) | Edge Vocabulary, Normalisierung, Aliases |
| **Unresolved Link** | [modules/unresolvedLink](modules/unresolvedLink.md) | Link-Handler, Adoption, Heading-Normalisierung |
| **Workbench** | [modules/workbench](modules/workbench.md) | Chain Workbench, Todo-Generator, Status, Actions |
| **Lint** | [modules/lint](modules/lint.md) | Lint Engine, Rules, Findings |
| **UI** | [modules/ui](modules/ui.md) | Modals, Settings Tab, Entity Picker |
---
## Schnellzugriff nach Thema
## Kern-Dokumente
### Installation & Setup
→ [05_Installation_Deployment.md](./05_Installation_Deployment.md)
### Nutzung & Konfiguration
### Konfiguration
→ [02_Administratorhandbuch.md](./02_Administratorhandbuch.md)
→ [06_Konfigurationsdateien_Referenz.md](./06_Konfigurationsdateien_Referenz.md)
→ [Interview_Config_Guide.md](./Interview_Config_Guide.md) - Interview-Profile erstellen (GenAI-freundlich)
- [13_Erfassung_Causal_Ketten](13_Erfassung_Causal_Ketten.md) **Praxis-Handbuch:** Elemente erfassen und Causal-Ketten schließen
- [01_Benutzerhandbuch](01_Benutzerhandbuch.md) Endnutzer-Workflows, Commands, Einstellungen
- [07_Event_Handler_Commands](07_Event_Handler_Commands.md) alle Commands und Event-Handler
- [02_Administratorhandbuch](02_Administratorhandbuch.md) Plugin-Konfiguration, Pfade, Wartung
- [06_Konfigurationsdateien_Referenz](06_Konfigurationsdateien_Referenz.md) Format aller Config-Dateien, Gewichtung, Analysis Policies
- [Interview_Config_Guide](Interview_Config_Guide.md) interview_config.yaml (Profile, Steps, Loops, WP-26)
### Nutzung
→ [01_Benutzerhandbuch.md](./01_Benutzerhandbuch.md)
→ [07_Event_Handler_Commands.md](./07_Event_Handler_Commands.md)
### Technik & Konzepte
### Entwicklung
→ [03_Entwicklerhandbuch.md](./03_Entwicklerhandbuch.md)
→ [04_Architektur.md](./04_Architektur.md)
- [04_Architektur](04_Architektur.md) System-Design, Komponenten, Datenfluss
- [03_Entwicklerhandbuch](03_Entwicklerhandbuch.md) Projekt-Struktur, Build, Testing
- [02_concepts/03_chain_identification_and_matching](02_concepts/03_chain_identification_and_matching.md) Template Matching, Scoring, Confidence
- [02_causal_chain_retrieving](02_causal_chain_retrieving.md) Kausale Ketten-Retrieval
### WP-26 Integration
→ [WP26_Plugin_Interface_Specification.md](./WP26_Plugin_Interface_Specification.md)
→ [WP26_Implementation_Checklist.md](./WP26_Implementation_Checklist.md)
### Operation & Deployment
- [05_Installation_Deployment](05_Installation_Deployment.md) Installation, Deployment
---
## Spezialthemen
- [12_Heading_Block_Link_Recommendation](12_Heading_Block_Link_Recommendation.md) Überschrift vs. Block-Link, Normalisierung, Obsidian-Verhalten
- [09_Workbench_Analysis_Basis](09_Workbench_Analysis_Basis.md) Workbench-Analyse
- [10_Workbench_Findings_Integration](10_Workbench_Findings_Integration.md) Findings-Integration
- [DANGLING_TARGET_CASES](DANGLING_TARGET_CASES.md) Dangling-Target-Fälle
---
## WP-26 Integration
- [06_LH_WP26_Plugin_Integration](06_LH_WP26_Plugin_Integration.md) Lastenheft für WP-26 Plugin-Integration
- [WP26_Plugin_Interface_Specification](WP26_Plugin_Interface_Specification.md) Schnittstellenspezifikation
- [WP26_Implementation_Checklist](WP26_Implementation_Checklist.md) Implementierungs-Checkliste
---
## Reports & Archive
- [CHAIN_INSPECTOR_V042_REPORT](CHAIN_INSPECTOR_V042_REPORT.md) aktuellster Chain Inspector Report
- Weitere Reports: [CHAIN_INSPECTOR_V0_REPORT](CHAIN_INSPECTOR_V0_REPORT.md) … [CHAIN_INSPECTOR_V04_REPORT](CHAIN_INSPECTOR_V04_REPORT.md)
- Legacy: [readme](readme.md), [Handbuch](Handbuch.md), [TESTING_WITH_REAL_VAULT](TESTING_WITH_REAL_VAULT.md)
---
## Vollständigkeits-Checkliste (Stand)
| Bereich | Status |
|---------|--------|
| Module | 14 Module dokumentiert |
| Commands | 11+ Commands |
| Settings | 40+ Settings |
| Config-Dateien | 6 Dateien (edge_vocabulary, graph_schema, interview_config, chain_roles, chain_templates, analysis_policies) |
| Scoring & Gewichtung | Template Matching + Profile + Analysis Policies |
| Heading-Normalisierung | normalizeHeadingForMatch (^Block-ID entfernen, Idempotenz ohne ^) |
---

View File

@ -1,5 +1,10 @@
# Chain-Identifikation und Template Matching
> **Stand:** Januar 2026
> **Siehe auch:** [modules/analysis](../modules/analysis.md), [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md)
---
## Übersicht
Das System identifiziert und füllt Chains durch einen mehrstufigen Prozess:
@ -176,13 +181,17 @@ roleEvidence.push({
### Scoring (`scoreAssignment`)
**Slot-Scoring**:
- Jeder zugewiesene Slot: **+2 Punkte**
**Implementierung:** `src/analysis/templateMatching.ts`
**Link-Scoring**:
- Erfüllter Link (mit erlaubter Role): **+10 Punkte**
- Fehlender Link (wenn `required_links: true`): **-5 Punkte**
- Falsche Role (wenn `required_links: true`): **-5 Punkte**
| Komponente | Punkte |
|------------|--------|
| Jeder zugewiesene Slot | +2 |
| Intra-Note-Bonus (alle Slots in derselben Note) | +5 |
| Erfüllter Link (mit erlaubter Edge-Role) | +10 |
| Fehlender Link (wenn `required_links: true`) | -5 |
| Falsche Role (wenn `required_links: true`) | -5 |
**Heading-Matching:** Vergleiche via `headingsMatch` (normalisiert `^BlockID`; siehe [12_Heading_Block_Link_Recommendation](../../12_Heading_Block_Link_Recommendation.md)).
### Confidence-Berechnung
@ -221,6 +230,8 @@ min_slots_filled_for_gap_findings: 3
min_score_for_gap_findings: 18
```
**Profil-Auswahl:** Plugin-Setting `templateMatchingProfile` (`discovery` | `decisioning`). Profil-Definitionen in `chain_templates.yaml` unter `defaults.profiles`.
### Profile-Auflösung
1. **Settings**: Plugin-Einstellung `templateMatchingProfile`
@ -372,6 +383,23 @@ Definiert Note-Type-Kompatibilität:
- Welche Edge-Typen sind typisch für `insight → decision`?
- Welche Edge-Typen sind verboten?
### `analysis_policies.yaml`
Definiert Severity-Policies pro Profil (discovery, decisioning) für Findings:
- `missing_link_constraints`, `weak_chain_roles`, `unmapped_edge_types`, `missing_slot_*`
---
## 11. Matching-Parameter (defaults.matching)
| Parameter | Standard | Beschreibung |
|-----------|----------|--------------|
| `distinct_nodes` | true | Jeder Knoten nur einmal pro Template |
| `required_links` | true | Links erforderlich (profil-überschreibbar) |
| `max_candidate_nodes` | 30 | Max. Candidate Nodes pro Template |
| `max_matches_per_template` | 2 | Max. unterschiedliche Zuordnungen (z. B. intra-note + cross-note) |
| `max_assignments_collected` | 1000 | Schleifenschutz beim Backtracking |
---
## Zusammenfassung

View File

@ -531,58 +531,75 @@ templates:
### Zweck
Definiert Policies für Findings (Severity, Unterdrückung, etc.).
Definiert Severity-Policies für Findings pro Profil (discovery, decisioning).
### Format
**YAML-Datei** (geplant, noch nicht vollständig implementiert).
**YAML-Datei** mit `profiles` und `severities` je Finding-Typ.
### Geplante Struktur
### Aktuelle Struktur
```yaml
version: "1.0"
findings:
missing_slot_*:
default_severity: warn
version: 1
profiles:
discovery:
severity: info
suppress_if:
- slots_filled < 2
severities:
missing_link_constraints: info
weak_chain_roles: info
unmapped_edge_types: info
missing_slot_*: info
decisioning:
severity: warn
suppress_if: []
dangling_target:
default_severity: error
suppress_if: []
missing_link_constraints:
default_severity: warn
suppress_if:
- required_links == false
no_causal_roles:
default_severity: info
suppress_if: []
severities:
missing_link_constraints: warn
weak_chain_roles: info
unmapped_edge_types: warn
missing_slot_*: warn
```
### Felder (geplant)
### Felder
#### Root-Level
- **`version`** (number, optional): Config-Version
- **`profiles`** (object, required): Profil-Name → Severity-Overrides
- **`severities`** (object): Finding-Code → Severity (`info`, `warn`, `error`)
- **`version`** (string, optional): Config-Version
- **`findings`** (object, required): Mapping von Finding-Codes zu Policies
### Verwendung
#### Finding Policy
- **Severity Policy:** `severityPolicy.ts` nutzt diese Datei für Profil-spezifische Severities
- **Profil-Auswahl:** Plugin-Setting `templateMatchingProfile` (`discovery` | `decisioning`)
- **`default_severity`** (string, required): Standard-Severity (`error`, `warn`, `info`)
- **`profiles`** (object, optional): Profil-spezifische Overrides
- **`discovery`** (object, optional): Discovery-Profil-Parameter
- **`decisioning`** (object, optional): Decisioning-Profil-Parameter
- **`suppress_if`** (array, optional): Bedingungen für Unterdrückung
---
### Status
## Template-Matching-Gewichtung (chain_templates.yaml)
**Noch nicht vollständig implementiert.** Aktuell wird Severity-Policy teilweise in `severityPolicy.ts` gehandhabt.
### Scoring-Logik (Implementierung)
Die Scoring-Logik ist in `src/analysis/templateMatching.ts` implementiert:
| Komponente | Punkte |
|------------|--------|
| Jeder zugewiesene Slot | +2 |
| Intra-Note-Bonus (alle Slots in derselben Note) | +5 |
| Erfüllter Link (mit erlaubter Edge-Role) | +10 |
| Fehlender Link (wenn `required_links: true`) | -5 |
| Falsche Role (wenn `required_links: true`) | -5 |
### Profil-Parameter (defaults.profiles)
| Parameter | discovery | decisioning | Beschreibung |
|-----------|-----------|-------------|--------------|
| `required_links` | false | true | Links sind Pflicht |
| `min_slots_filled_for_gap_findings` | 2 | 3 | Min. Slots für Gap-Findings |
| `min_score_for_gap_findings` | 8 | 18 | Min. Score für Gap-Findings |
### Matching-Parameter (defaults.matching)
| Parameter | Standard | Beschreibung |
|-----------|----------|--------------|
| `distinct_nodes` | true | Jeder Knoten nur einmal pro Template |
| `required_links` | true | Links erforderlich (profil-überschreibbar) |
| `max_candidate_nodes` | 30 | Max. Candidate Nodes pro Template |
| `max_matches_per_template` | 2 | Max. unterschiedliche Zuordnungen pro Template |
| `max_assignments_collected` | 1000 | Schleifenschutz beim Backtracking |
---

View File

@ -42,16 +42,13 @@ Bei einer Zeile `## Überschrift ^Block` in der Zieldatei:
**Fazit:** Der Interview-Assistent erzeugt `[[Note#Überschrift]]`. In Obsidian springt dieser Link **in die Note, aber nicht auf die Überschrift** (siehe Abschnitt 1). Obsidian-typisches Springen auf die Überschrift (inkl. Highlight des Abschnitts) liefert nur `[[Titel#Überschrift Block]]` (UI-erzeugt, ohne `^`).
### 2.3 Inspect Chains & Chain Workbench
### 2.3 Inspect Chains & Chain Workbench (Stand: Jan 2026)
- **chainInspector** vergleicht überall **strikt:**
`edge.target.heading === context.heading` und
`edge.source.sectionHeading === context.heading`.
- **dangling_target_heading:** Prüfung gegen `metadataCache.getFileCache(...).headings` mit
`h.heading === targetHeading` (ebenfalls exakt).
- **Chain Workbench** nutzt dieselben Konzepte (context/assignment mit `heading`); keine Normalisierung.
**Folge:** Sobald eine Seite `"Überschrift ^block"` und ein Link `[[Titel#Überschrift]]` vorkommt (oder umgekehrt), stimmen die Strings nicht überein → kein Match, ggf. falscher `dangling_target_heading`.
- **headingsMatch / normalizeHeadingForMatch** (`src/unresolvedLink/linkHelpers.ts`):
- **Mit ^Block-ID:** `"Überschrift ^Block"``"Überschrift"` (Block-Suffix entfernt)
- **Ohne ^:** Rückgabe unverändert (Idempotenz für bereits normalisierte Formen)
- **Matchen:** `"Überschrift ^Block"` und `"Überschrift"` gelten als dieselbe Sektion.
- **Nicht unterstützt:** `"Überschrift Block"` (Obsidian-UI, ohne ^) wird **nicht** zu `"Überschrift"` normalisiert für zuverlässiges Matching Links mit `^` verwenden.
---
@ -75,30 +72,24 @@ Um nicht zu viele Änderungen gleichzeitig zu öffnen:
- **Nur eine Baustelle:** **Normalisierung in Inspect Chains & Chain Workbench** (zentrale Normalisierungsfunktion + alle Heading-Vergleiche und `dangling_target_heading` auf normalisierte Form umstellen).
- **Interview-Assistent vorerst unverändert:** Weiter `[[Note#Überschrift]]` ausgeben. Eine spätere, **optionale** Anpassung (z.B. Ausgabe `[[Note#Überschrift Block]]` im Obsidian-Stil, damit der Klick auf die Überschrift springt) kann separat erfolgen.
### 4.2 Normalisierungsregel (für Vergleiche)
### 4.2 Umsetzte Normalisierungsregel (Stand: Jan 2026)
Drei Schreibweisen sollen als **dieselbe Sektion** gelten:
**Implementiert in `normalizeHeadingForMatch` (linkHelpers.ts):**
- `Überschrift` (Interview, reiner Text)
- `Überschrift ^Block` (Quelldokument / sectionParser)
- `Überschrift Block` (Obsidian-UI-Link, ohne `^`)
1. **Mit ^Block-ID:** Block-Suffix entfernen: `\s*\^[a-zA-Z0-9_-]+\s*$` → z.B. `"Überschrift ^Block"``"Überschrift"`, `"Lars ist gut ^PersLars"``"Lars ist gut"`.
2. **Ohne ^:** Rückgabe unverändert (Idempotenz). Multi-Wort-Überschriften wie `"Lars ist gut"` werden nicht gekürzt.
**Kanonische Form für Vergleiche:**
**Matchen:** `"Überschrift ^Block"` und `"Überschrift"` gelten als dieselbe Sektion. `"Lars ist gut ^PersLars"` und `"Lars ist gut"` matchen.
1. Am Ende **Block-Suffix mit Caret** entfernen: `\s+\^[a-zA-Z0-9_-]+$` → z.B. `"Überschrift ^Block"``"Überschrift"`.
2. Danach **ein einzelnes Wort am Ende** (nur Buchstaben, Zahlen, Bindestrich, Unterstrich) optional entfernen: `\s+[a-zA-Z0-9_-]+$` → z.B. `"Überschrift Block"``"Überschrift"`.
**Nicht unterstützt:** `"Überschrift Block"` (Obsidian-UI, ohne ^) wird **nicht** automatisch zu `"Überschrift"` normalisiert zur Vermeidung von Fehlmatches bei kanonischen Multi-Wort-Überschriften. **Empfehlung:** Für zuverlässiges Matching Links mit `^` verwenden: `[[Note#Überschrift ^Block]]`.
So werden alle drei Varianten auf `"Überschrift"` abgebildet. **Randfall:** Eine Überschrift, die wirklich „X Y“ heißt (ohne Block-ID), würde ebenfalls zu „X“ normalisiert und könnte mit „X“ matchen; in der Praxis selten. Die Funktion **nur** für Vergleiche/Prüfungen nutzen, nicht für Anzeige oder gespeicherte Links.
### 4.3 Umsetzte Maßnahmen
### 4.3 Konkrete Maßnahmen (nur diese eine Baustelle)
1. **Zentrale Normalisierung:** `normalizeHeadingForMatch` und `headingsMatch` in `src/unresolvedLink/linkHelpers.ts` nur ^Block-Suffix entfernen, sonst Idempotenz.
1. **Eine zentrale Normalisierungsfunktion** (z.B. in `linkHelpers` oder eigenem Modul): Eingabe = Überschrift-String, Ausgabe = kanonische Form wie oben.
2. **Inspect Chains & Chain Workbench:** Nutzen `headingsMatch` für alle Heading-Vergleiche.
2. **Inspect Chains (chainInspector):** Alle Vergleiche mit `heading` / `sectionHeading` auf die **normalisierte** Form umstellen; bei **dangling_target_heading** `targetHeading` und Cache-Headings vor dem Vergleich normalisieren.
3. **Chain Workbench:** Dieselbe Normalisierung überall, wo Headings verglichen werden.
4. **graphIndex / Interview:** Keine Änderung in dieser Baustelle.
3. **Interview-Assistent:** Unverändert erzeugt weiterhin `[[Note#Überschrift]]`.
### 4.4 Optional später (separate Baustelle)
@ -106,8 +97,8 @@ So werden alle drei Varianten auf `"Überschrift"` abgebildet. **Randfall:** Ein
---
## 5. Kurzfassung
## 5. Kurzfassung (Stand: Jan 2026)
- **Obsidian (beobachtet):** Manueller Link auf Überschrift mit Block-ID → `[[Titel#Überschrift Block]]` (ohne `^`). Klick springt auf Überschrift und highlighted den Abschnitt. `[[Titel#Überschrift]]` springt nur in die Note, nicht auf die Überschrift. `[[Titel#^Block]]` highlighted nur die Überschrift.
- **Eine Baustelle:** Nur **Normalisierung in Inspect Chains & Chain Workbench** umsetzen (zentrale Funktion: Block-Suffix mit `^` und ggf. ein trailinges Wort entfernen; alle Heading-Vergleiche und `dangling_target_heading` auf diese Form umstellen). Interview-Assistent und graphIndex in dieser Runde **nicht** anfassen.
- **Optional später:** Interview-Assistent so anpassen, dass er z.B. `[[Note#Überschrift Block]]` erzeugt (Obsidian-Stil), damit der Klick auf den Link auf die Überschrift springt.
- **Plugin-Normalisierung:** `normalizeHeadingForMatch` entfernt nur `^BlockID`; bei Überschriften ohne `^` wird unverändert zurückgegeben (Idempotenz). Dadurch matchen `"Lars ist gut ^PersLars"` und `"Lars ist gut"` korrekt.
- **Empfehlung:** Für zuverlässiges Matching Links mit `^` verwenden: `[[Note#Überschrift ^Block]]`. Obsidian-Form `[[Titel#Überschrift Block]]` (ohne `^`) wird nicht automatisch normalisiert.
- **Obsidian (beobachtet):** Manueller Link → `[[Titel#Überschrift Block]]` (ohne `^`). Klick springt auf Überschrift. `[[Titel#^Block]]` highlighted nur die Überschrift.

View File

@ -0,0 +1,309 @@
# Handbuch: Erfassung & Causal-Ketten in Obsidian
> **Zielgruppe:** Du beim täglichen Erfassen (auch unterwegs)
> **Plugin:** Mindnet Causal Assistant
> **Stand:** August 2026
> **Zweck:** Elemente so anlegen und vernetzen, dass kausale Ketten erkennbar, prüfbar und schließbar sind.
---
## 1. Was du erzeugst
Mindnet speichert Wissen als **Markdown im Vault**. Das Plugin hilft, daraus einen **kausalen Graphen** zu machen.
| Baustein | Bedeutung | Beispiel |
|----------|-----------|----------|
| **Note** | Eine Entität (Datei) mit Typ und `id` | Experience „Geburt der Kinder“ |
| **Section** | Abschnitt unter einer Überschrift; optional mit Typ | `## Learning ^learning` + `[!section] insight` |
| **Block-ID** | Anker hinter der Überschrift (`^…`) | `^learning`, `^next` |
| **Edge** | Gerichtete Beziehung mit Typ | `caused_by`, `guides`, `resulted_in` |
| **Kette** | Mehrere Notes/Sections + Edges in einem Muster | Experience → Insight → Decision |
**Merksatz:** Text allein reicht nicht. Ketten entstehen, wenn du **Rollen** (Typen) und **Kanten** (Edges) setzt.
### Urlaub / Weiterarbeit ohne Massen-Relink
Neue Notes so anlegen, dass sie später **nicht** umgeschrieben werden müssen. Alte Notes nicht anfassen, nur neue sauber schreiben.
| Immer so | Nie so (später teuer) |
|----------|------------------------|
| Überschrift mit Block-ID: `## Learning ^learning` | Überschrift ohne `^…` |
| Section-Typ: `> [!section] insight` | Nur Fließtext ohne Typ |
| Intra-Note: `[[#^learning]]` | `[[#Learning]]` ohne `^` |
| Cross-Note: `[[Note#Überschrift ^block]]` | `[[Note#Überschrift]]` ohne Block-ID |
| Kanten als `[!edge]` mit kanonischem Typ (`guides`, `caused_by`, `derived_from`) | Nur lose Wikilinks oder Alias-Typen wie `derives` |
**Create note from profile** und **Chain Workbench** schreiben diese Form jetzt selbst. Manuelle Obsidian-UI-Links (`[[Note#Überschrift Block]]` ohne `^`) vermeiden.
```mermaid
flowchart LR
E[Experience / Situation] -->|causal / influences| I[Insight / Learning]
I -->|guides / foundation_for| D[Decision / nächster Schritt]
D -->|causal| E2[neue Experience / Feedback]
```
---
## 2. Die wichtigsten Commands
Command Palette: `Ctrl+P` (macOS: `Cmd+P`).
| Wann | Command |
|------|---------|
| Neu anfangen | **Mindnet: Create note from profile** |
| Links zu Edges machen | **Mindnet: Build semantic mapping blocks (by section)** |
| Edge-Typ wählen/ändern | **Mindnet: Edge-Type ändern** |
| Note prüfen | **Mindnet: Validate current note** |
| Ketten verstehen | **Mindnet: Inspect Chains (Current Section)** |
| Lücken schließen | **Mindnet: Chain Workbench (Current Section)** |
| Vault-weit Lücken finden | **Mindnet: Scan Vault for Chain Gaps** |
| Findings beheben | **Mindnet: Fix Findings (Current Section)** |
Einstellungen: **Mindnet: Einstellungen öffnen** (oder Plugin-Name in der linken Settings-Leiste).
---
## 3. Alltags-Workflow: von der Idee zur Kette
### Schritt A — Element anlegen
1. **Mindnet: Create note from profile**
2. Profil wählen, z.B.:
- **Experience Basis** → Erlebnis / Situation
- **Insight Basis** → Erkenntnis / Learning
- weitere Profile je nach Vault-Config
3. Titel und Ordner setzen → Note öffnet sich
4. Interview-Wizard ausfüllen → **Review → Apply & Finish**
Ergebnis typischerweise:
- Frontmatter mit `id`, `title`, `type`, …
- Sections mit Überschriften
- oft schon `[!section] …` und `^block-id` (WP-26)
> **Tipp:** **Mindnet: Create note from profile** startet den Wizard immer. Für eine schon offene Note: **Mindnet: Interview für aktuelle Note starten**.
### Schritt B — Verbindungen setzen (während oder nach dem Schreiben)
**Variante 1 im Text verlinken, dann mappen**
1. Im Fließtext oder am Section-Ende Wikilinks setzen, z.B. `[[Meine Einsicht]]`
2. Cursor in die Section
3. **Mindnet: Edge-Type ändern** → passenden Typ wählen
oder zuerst Links setzen und danach **Build semantic mapping blocks**
4. Mapping-Block prüfen:
```markdown
> [!abstract] 🕸️ Semantic Mapping
>
> > [!edge] caused_by
> > [[Früheres Erlebnis]]
>
> > [!edge] guides
> > [[Nächster Schritt ^next]]
```
**Variante 2 fehlende Note aus einem Link erzeugen**
1. `[[Noch nicht existierende Note]]` schreiben
2. Link anklicken (Reading View: Klick; Editor oft `Ctrl`+Klick)
3. Profil wählen → Note (+ optional Wizard)
**Variante 3 Intra-Note (innerhalb einer Datei)**
Sections derselben Note verbinden, bevorzugt mit Block-ID:
```markdown
## Situation ^sit
> [!section] experience
## Learning ^learning
> [!section] insight
> [!edge] derived_from
> [[#^sit]]
## Nächster Schritt ^next
> [!section] decision
> [!edge] guides
> [[#^learning]]
```
**Link-Empfehlung für zuverlässiges Matching:**
| Form | Nutzen |
|------|--------|
| `[[Note#Überschrift ^block]]` oder `[[#^block]]` | zuverlässig für Chain Inspector / Workbench |
| `[[Note#Überschrift]]` | oft vom Interview erzeugt; springt in Obsidian nicht immer zur Überschrift |
| Obsidian-UI ohne `^` (`…#Überschrift Block`) | gut zum Navigieren, Matching kann schwächer sein |
### Schritt C — Kette prüfen
1. Cursor in die relevante Section stellen
2. **Mindnet: Chain Workbench (Current Section)** öffnen
3. Matches und Status lesen (siehe Abschnitt 5)
4. Todos abarbeiten: fehlende Edges, fehlende Notes, falsche Typen
5. Optional: **Inspect Chains** für den textuellen Report (Console / Report)
### Schritt D — Qualität sichern
1. **Validate current note** → keine Errors
2. Bei Bedarf **Fix Findings**
3. Speichern — Obsidian Sync übernimmt den Vault
---
## 4. Welche Kette willst du erzählen?
Das Plugin matcht gegen **Chain Templates** (Datei `chain_templates.yaml`). Du musst sie nicht auswendig kennen — aber die Kernmuster helfen beim Erfassen:
### 4.1 Trigger → Transformation → Outcome
**Frage:** Was hat mich verändert, und was folgte daraus?
| Slot | Typische Typen | Deine Rolle beim Erfassen |
|------|----------------|---------------------------|
| trigger | experience, event, obstacle, risk, state | Auslöser / Situation |
| transformation | insight, belief, value, principle, … | innere Veränderung |
| outcome | decision, project, habit, goal | Folge / Handlung |
Edges zwischen den Slots: eher **causal**, **influences**, **provenance** (`foundation_for` u. ä.).
### 4.2 Learning Loop
**Frage:** Erlebnis → Lernen → Verhalten → neues Erlebnis?
| Slot | Typische Typen |
|------|----------------|
| experience | experience, event, situation |
| learning | insight, principle, value, … |
| behavior | habit, decision, task |
| feedback | experience, journal, event, state |
Viele Experience-Interviews legen genau diese Abschnitte nahe (Situation → Learning → Nächster Schritt).
### 4.3 Decision Logic
**Frage:** Was treibt die Entscheidung, was begrenzt sie, was entsteht?
driver + constraint → decision → outcome
### 4.4 Weitere Muster (kurz)
- **constraint_to_adaptation** — Problem → Anpassung → Stabilisierung
- **person_influence** — Person → inneres Modell → Entscheidung/Outcome
- **state_trigger_response** — Zustand → Auslöser → Reaktion → neuer Zustand
**Praxisregel:** Beim Schreiben eine dieser Geschichten im Kopf behalten und die fehlenden Glieder als Notes/Sections + Edges nachziehen — nicht „irgendwelche Links“.
---
## 5. Workbench lesen: Status & Todos
| Status | Bedeutung | Typische Aktion |
|--------|-----------|-----------------|
| **complete** | Slots und geforderte Links passen | fertig / nur noch Feinschliff |
| **near_complete** | fast vollständig | 12 Todos (Edge oder Slot) |
| **partial** | mehrere Lücken | gezielt fehlende Glieder anlegen |
| **weak** | schwache/unsichere Zuordnung | Typen und Edges prüfen, Template evtl. falsch |
Häufige Todos:
- **missing_slot** — fehlendes Glied der Kette (Note oder Section anlegen)
- **missing_link** — Verbindung zwischen vorhandenen Gliedern fehlt → Edge einfügen
- **dangling_target** — Link zeigt ins Leere → Note/Heading erzeugen oder Link retargeten
**Arbeitsweise:** Immer zuerst **near_complete** schließen — dort ist der Erkenntnisgewinn pro Klick am höchsten. Vault-weit: **Scan Vault for Chain Gaps**.
---
## 6. Section Types & Effective Type
Innerhalb einer Note können Abschnitte **eigene Typen** haben:
```markdown
---
type: experience
---
## Learning ^learning
> [!section] insight
Was ich daraus lerne …
```
- Ohne `[!section]` gilt der **Note-Type** (`experience`).
- Mit `[!section] insight` zählt die Section als **insight** fürs Template-Matching (**effective type**).
- So kann eine Experience-Note intern schon die Learning-Loop abbilden, ohne sofort drei Dateien anzulegen.
**Wann eigene Datei, wann Section?**
| Situation | Empfehlung |
|-----------|------------|
| Ein Erlebnis mit Situation / Reaktion / Learning / nächstem Schritt | eine Note, Sections + Intra-Note-Edges |
| Learning soll später vielen Experiences gehören | eigene Insight-Note + Cross-Note-Edge |
| Entscheidung betrifft mehrere Projekte | eigene Decision-Note |
---
## 7. Mini-Checkliste pro Erfassungs-Session
Vor dem Schließen der Note:
- [ ] Frontmatter hat `id` und sinnvollen `type`
- [ ] Wichtige Abschnitte haben Überschrift (+ idealerweise `^block-id`)
- [ ] Wo nötig: `[!section] …` gesetzt
- [ ] Beziehungen liegen als `[!edge]` (Mapping-Block) vor — nicht nur lose Wikilinks
- [ ] Mindestens eine erkennbare Ketten-Idee (z.B. Learning Loop oder Trigger→Outcome)
- [ ] **Validate** ohne Errors
- [ ] Bei Ambition: Workbench zeigt **near_complete** oder **complete** für das Haupt-Template
---
## 8. Typischer Tagesablauf (Urlaub / unterwegs)
1. **Erfassen** — 13 Experiences oder Insights über Profile + Interview
2. **Verknüpfen** — Edges zu bestehenden Notes/Sections (Mapping / Edge-Type)
3. **Ketten schließen** — Workbench auf der wichtigsten Section, 515 Minuten Todos
4. **Optional Scan** — einmal am Tag/Woche Vault Gap Scan, nahe Lücken priorisieren
Obsidian Sync speichert den Vault zentral. Plugin-Updates (`main.js`) synct nur, wenn Community-Plugins in Sync aktiviert sind — sonst nach Build manuell in `.obsidian/plugins/mindnet-causal-assistant/` legen.
---
## 9. Häufige Stolpersteine
| Problem | Was tun |
|---------|---------|
| Keine Profile beim Create | Config-Pfad `_system/dictionary/interview_config.yaml` / Sync prüfen |
| Workbench „Chain Roles/Templates nicht geladen“ | Pfade in Plugin-Settings prüfen |
| Kette wird nicht erkannt | Edge-Typ zu generisch/falsch; Section-Type fehlt; Link ohne treffendes Heading/`^block` |
| Link springt nicht zur Section | Form mit Block-ID nutzen (`[[#^id]]` oder Heading+`^block`) |
| Mapping an falscher Stelle | Cursor in die richtige Section; Mapping erneut bauen |
---
## 10. Kurz-Rezept: „Ich will eine Learning-Loop sauber machen“
1. Experience-Note aus Profil **Experience Basis**
2. Interview: Situation, Reaktion, Learning, Nächster Schritt ausfüllen
3. Prüfen: Sections haben Block-IDs; Learning = `[!section] insight` (falls vorgesehen)
4. Edges setzen, z.B. Learning ← Situation, Nächster Schritt ← Learning (`guides` / passende Rolle)
5. Cursor auf Learning-Section → **Chain Workbench** → Template `loop_learning` / verwandte Matches
6. Todos bis **near_complete** oder **complete**
7. Validate → speichern
---
## Siehe auch
- [01_Benutzerhandbuch](01_Benutzerhandbuch.md) — Commands & Settings im Detail
- [07_Event_Handler_Commands](07_Event_Handler_Commands.md) — vollständige Command-Referenz
- [12_Heading_Block_Link_Recommendation](12_Heading_Block_Link_Recommendation.md) — Link-Formen & Matching
- [Interview_Config_Guide](Interview_Config_Guide.md) — Profile anpassen (Admin)
- [02_concepts/03_chain_identification_and_matching](02_concepts/03_chain_identification_and_matching.md) — Matching-Technik
**Tipp:** Diese Datei nach `_system/docs/` (oder ähnlich) im Vault kopieren, dann liegt sie über Obsidian Sync auf allen Geräten.

81
docs/by_role/README.md Normal file
View File

@ -0,0 +1,81 @@
# Dokumentation nach Rolle
> **Zielgruppe:** Alle
> **Einstieg:** Wählen Sie Ihre Rolle und folgen Sie den verlinkten Dokumenten
---
## Nutzer
**Primäre Dokumente:**
- [13_Erfassung_Causal_Ketten](../13_Erfassung_Causal_Ketten.md) **Alltag:** Erfassen, verlinken, Causal-Ketten prüfen/schließen
- [01_Benutzerhandbuch](../01_Benutzerhandbuch.md) Installation, Commands, Workflows, Troubleshooting
- [07_Event_Handler_Commands](../07_Event_Handler_Commands.md) alle Commands und Event-Handler
**Zusätzlich:**
- [05_Installation_Deployment](../05_Installation_Deployment.md) Installation
- [12_Heading_Block_Link_Recommendation](../12_Heading_Block_Link_Recommendation.md) Überschriften-Links (Obsidian)
---
## Administrator
**Primäre Dokumente:**
- [02_Administratorhandbuch](../02_Administratorhandbuch.md) Plugin-Konfiguration, Pfade, Wartung
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md) alle Config-Dateien, Gewichtung, Analysis Policies
- [Interview_Config_Guide](../Interview_Config_Guide.md) interview_config.yaml
**Zusätzlich:**
- [05_Installation_Deployment](../05_Installation_Deployment.md) Deployment
---
## Entwickler
**Primäre Dokumente:**
- [03_Entwicklerhandbuch](../03_Entwicklerhandbuch.md) Projekt-Struktur, Build, Testing
- [04_Architektur](../04_Architektur.md) System-Design
- [modules/README](../modules/README.md) Modul-Übersicht
**Zusätzlich:**
- [02_concepts/03_chain_identification_and_matching](../02_concepts/03_chain_identification_and_matching.md) Template Matching, Scoring
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md) Config-Formate
---
## Architekt
**Primäre Dokumente:**
- [04_Architektur](../04_Architektur.md) Komponenten, Datenfluss
- [02_concepts/03_chain_identification_and_matching](../02_concepts/03_chain_identification_and_matching.md) Chain-Identifikation, Scoring
- [02_causal_chain_retrieving](../02_causal_chain_retrieving.md) Kausale Ketten
**Zusätzlich:**
- [modules/](../modules/) Modul-Details
---
## Projektleitung
**Primäre Dokumente:**
- [04_Architektur](../04_Architektur.md) Überblick
- [README](../../README.md) Projekt-README
- [STATUS_DOD](../../STATUS_DOD.md) Definition of Done
---
## Systemadmin
**Primäre Dokumente:**
- [05_Installation_Deployment](../05_Installation_Deployment.md) Installation, Deployment
- [02_Administratorhandbuch](../02_Administratorhandbuch.md) Konfiguration

58
docs/modules/README.md Normal file
View File

@ -0,0 +1,58 @@
# Module technische Dokumentation
> **Zielgruppe:** Entwickler, Architekten
> **Übersicht:** Modul-basierte Dokumentation des Mindnet Causal Assistant Plugins
---
## Modul-Übersicht
| Modul | Pfad in `src/` | Kurzbeschreibung |
|-------|----------------|------------------|
| [Analysis](analysis.md) | `analysis/` | Chain Inspector, Template Matching, Scoring, Findings |
| [Dictionary](dictionary.md) | `dictionary/` | Config-Loader, Chain Roles, Chain Templates |
| [Interview](interview.md) | `interview/` | Wizard, Loop State, Renderer, Section Key Resolver |
| [Mapping](mapping.md) | `mapping/` | Semantic Mapping, Edge Type Selector |
| [Parser](parser.md) | `parser/` | Edges aus Callouts, Frontmatter, Links |
| [Graph](graph.md) | `graph/` | Graph Builder, Traversal, Resolution |
| [Vocabulary](vocabulary.md) | `vocab/` | Edge Vocabulary, Normalisierung |
| [Unresolved Link](unresolvedLink.md) | `unresolvedLink/` | Link-Handler, Adoption, Heading-Normalisierung |
| [Workbench](workbench.md) | `workbench/` | Chain Workbench, Todo-Generator, Status |
| [Lint](lint.md) | `lint/` | Lint Engine, Rules |
| [UI](ui.md) | `ui/` | Modals, Settings Tab, Entity Picker |
---
## Abhängigkeiten (vereinfacht)
```
main.ts
├── analysis ← dictionary, graph, parser
├── commands ← analysis, graph, mapping, ui, ...
├── dictionary ← vault
├── graph ← parser
├── interview ← dictionary, parser
├── mapping ← parser, schema
├── parser
├── vocab ← vault
├── lint ← vocab, parser
├── unresolvedLink ← interview, entityPicker
├── workbench ← analysis, interview, mapping
└── ui
```
---
## Weitere Module
- **schema/** Graph Schema Loader
- **entityPicker/** Note Index, Folder Tree, Filters
- **export/** Graph Export
- **utils/** Logger
---
## Siehe auch
- [04_Architektur](../04_Architektur.md) System-Design
- [02_concepts/03_chain_identification_and_matching](../02_concepts/03_chain_identification_and_matching.md) Template Matching, Scoring

112
docs/modules/analysis.md Normal file
View File

@ -0,0 +1,112 @@
# Modul: Analysis
> **Pfad:** `src/analysis/`
> **Zielgruppe:** Entwickler, Architekten
---
## Übersicht
Das Analysis-Modul enthält die Chain Inspector-Logik, Template Matching, Scoring und Findings-Erzeugung.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `chainInspector.ts` | Haupt-Analyse-Engine |
| `templateMatching.ts` | Template Matching, Scoring, Backtracking |
| `graphIndex.ts` | Graph-Indexierung, Edge-Erfassung |
| `sectionContext.ts` | Section-Context-Handling |
| `severityPolicy.ts` | Severity-Policy für Findings |
---
## Scoring-Logik (Template Matching)
### scoreAssignment
Die Funktion `scoreAssignment` bewertet jede Slot-Zuordnung:
| Komponente | Punkte |
|------------|--------|
| Jeder zugewiesene Slot | +2 |
| Intra-Note-Bonus (alle Slots in derselben Note) | +5 |
| Erfüllter Link (mit erlaubter Edge-Role) | +10 |
| Fehlender Link (wenn `required_links: true`) | -5 |
| Falsche Role (wenn `required_links: true`) | -5 |
### Link-Validierung
- **findEdgeBetween:** Sucht Edge zwischen zwei Nodes (file:heading)
- **headingsMatch:** Vergleicht Überschriften (normalisiert über `normalizeHeadingForMatch`)
- **Role-Mapping:** Edge-Typ → Role via `chain_roles.yaml`
### Profile-Auflösung
1. **Settings:** `templateMatchingProfile` (`discovery` | `decisioning`)
2. **Template:** `template.matching.required_links`
3. **Defaults:** `defaults.matching.required_links`
---
## Template-Matching-Profile
| Profil | required_links | min_slots_filled_for_gap_findings | min_score_for_gap_findings |
|--------|----------------|-----------------------------------|----------------------------|
| `discovery` | false | 2 | 8 |
| `decisioning` | true | 3 | 18 |
Profile werden in `chain_templates.yaml` unter `defaults.profiles` definiert.
---
## Confidence-Berechnung
| Confidence | Bedingung |
|------------|-----------|
| `confirmed` | slotsComplete AND linksComplete AND mind. eine causal-ish Role |
| `plausible` | slotsComplete, aber (nicht linksComplete ODER keine causal-ish Role) |
| `weak` | nicht slotsComplete |
**causal-ish Roles:** `["causal", "influences", "enables_constraints"]` (konfigurierbar in `chain_templates.yaml`)
---
## Analysis Policies
Datei: `analysis_policies.yaml` (Pfad via Settings `analysisPoliciesPath`)
```yaml
version: 1
profiles:
discovery:
severities:
missing_link_constraints: info
weak_chain_roles: info
unmapped_edge_types: info
missing_slot_*: info
decisioning:
severities:
missing_link_constraints: warn
weak_chain_roles: info
unmapped_edge_types: warn
missing_slot_*: warn
```
---
## Abhängigkeiten
- **Dictionary:** Chain Roles, Chain Templates
- **Graph:** Graph Index, Traversal
- **Parser:** Edge-Extraktion
- **Unresolved Link:** `headingsMatch`, `normalizeHeadingForMatch`
---
## Siehe auch
- [02_concepts/03_chain_identification_and_matching](../02_concepts/03_chain_identification_and_matching.md)
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md) chain_templates, chain_roles, analysis_policies

View File

@ -0,0 +1,67 @@
# Modul: Dictionary
> **Pfad:** `src/dictionary/`
> **Zielgruppe:** Entwickler, Administratoren
---
## Übersicht
Das Dictionary-Modul lädt und parst alle Konfigurationsdateien (YAML, Markdown) und stellt sie den anderen Modulen zur Verfügung.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `DictionaryLoader.ts` | Generischer Loader mit Last-Known-Good |
| `ChainRolesLoader.ts` | Lädt chain_roles.yaml |
| `ChainTemplatesLoader.ts` | Lädt chain_templates.yaml |
| `ConfigPathManager.ts` | Pfad-Auflösung für Config-Dateien |
| `parseChainRoles.ts` | Parser für chain_roles |
| `parseChainTemplates.ts` | Parser für chain_templates |
| `types.ts` | TypeScript-Typen |
---
## Geladene Config-Dateien
| Datei | Pfad-Setting | Inhalt |
|-------|-------------|--------|
| edge_vocabulary | edgeVocabularyPath | Kanonische Edge-Typen, Aliases |
| graph_schema | graphSchemaPath | Empfehlungen für Edge-Typen |
| interview_config | interviewConfigPath | Profile, Steps, Loops |
| chain_roles | chainRolesPath | Edge-Typ → Role Mapping |
| chain_templates | chainTemplatesPath | Templates, Slots, Links, Profile |
| analysis_policies | analysisPoliciesPath | Severity-Policies pro Profil |
---
## Live-Reload
- **Debounced:** 200ms bei Dateiänderung
- **Last-Known-Good:** Bei Parse-Fehlern wird letzte gültige Config verwendet
- **Vault Modify Event:** Automatisches Neuladen bei Änderung im Vault
---
## Chain Templates Struktur
- **defaults.matching:** required_links, distinct_nodes, max_candidate_nodes, max_matches_per_template, max_assignments_collected
- **defaults.profiles:** discovery, decisioning (min_slots_filled_for_gap_findings, min_score_for_gap_findings)
- **templates:** name, slots, links, matching (optional)
---
## Chain Roles Struktur
- **roles:** Mapping Role-Name → edge_types[]
- **Standard-Rollen:** causal, influences, enables_constraints, provenance, epistemic, normative, structural, temporal
---
## Siehe auch
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md)
- [02_Administratorhandbuch](../02_Administratorhandbuch.md)

28
docs/modules/graph.md Normal file
View File

@ -0,0 +1,28 @@
# Modul: Graph
> **Pfad:** `src/graph/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Graph Building, Traversal, Target Resolution, Report Rendering.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `GraphBuilder.ts` | Baut Graph aus Vault |
| `GraphIndex.ts` | Indexierung für Traversal |
| `traverse.ts` | BFS/DFS Traversal |
| `resolveTarget.ts` | Link → File Auflösung |
| `renderChainReport.ts` | Report-Rendering |
---
## Siehe auch
- [analysis/graphIndex](../analysis.md) Graph Index für Chain Inspector

47
docs/modules/interview.md Normal file
View File

@ -0,0 +1,47 @@
# Modul: Interview
> **Pfad:** `src/interview/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Interview-Wizard zur strukturierten Erfassung von Inhalten, Section-basierte Ausgabe, Frontmatter-Generierung.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `InterviewConfigLoader.ts` | Lädt interview_config.yaml |
| `parseInterviewConfig.ts` | YAML → InterviewConfig Parser |
| `wizardState.ts` | Wizard State (Steps, Loops) |
| `loopState.ts` | Nested Loops |
| `renderer.ts` | Output-Rendering (Section-basiert) |
| `writeFrontmatter.ts` | Frontmatter-Generierung |
| `sectionKeyResolver.ts` | Section-Key-Auflösung |
| `extractTargetFromAnchor.ts` | Target-Extraktion aus Anchors |
| `targetTypeResolver.ts` | Note-Type- und Section-Type-Auflösung |
| `slugify.ts` | Slug-Generierung für Dateinamen |
---
## Step-Typen
- `capture_frontmatter`, `capture_text`, `capture_text_line`
- `loop`, `review`, `instruction`
- `llm_dialog`, `entity_picker` (experimentell)
---
## WP-26 Features
- `section_type`, `block_id`, `generate_block_id`, `references`
---
## Siehe auch
- [Interview_Config_Guide](../Interview_Config_Guide.md)

28
docs/modules/lint.md Normal file
View File

@ -0,0 +1,28 @@
# Modul: Lint
> **Pfad:** `src/lint/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Linting Engine für Note-Validierung, regelbasierte Prüfungen, Findings.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `LintEngine.ts` | Haupt-Engine |
| `rules/` | Einzelne Regeln |
| `rule_hub_has_causality.ts` | Kausale Edges prüfen |
| `rule_missing_target.ts` | Fehlende Targets |
| `rule_unknown_edge.ts` | Unbekannte Edge-Types |
---
## Command
`mindnet-validate-current-note` validiert aktuelle Note

32
docs/modules/mapping.md Normal file
View File

@ -0,0 +1,32 @@
# Modul: Mapping
> **Pfad:** `src/mapping/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Semantic Mapping Section-basierte Gruppierung von Links nach Edge-Typen, Edge-Type-Assignment.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `semanticMappingBuilder.ts` | Baut Mapping-Blöcke |
| `mappingExtractor.ts` | Extrahiert existierende Mappings |
| `mappingBuilder.ts` | Erstellt Mapping-Struktur |
| `edgeTypeSelector.ts` | Edge-Type-Auswahl (empfohlen via graph_schema) |
| `updateMappingBlocks.ts` | Aktualisiert Note |
| `sectionParser.ts` | Section-Parsing |
| `graphSchema.ts` | Schema-Lookup für Empfehlungen |
| `folderHelpers.ts` | Ordner-Hilfsfunktionen |
| `worklistBuilder.ts` | Worklist für Pending Assignments |
---
## Siehe auch
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md) graph_schema

37
docs/modules/parser.md Normal file
View File

@ -0,0 +1,37 @@
# Modul: Parser
> **Pfad:** `src/parser/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Markdown-Parsing: Edges aus Callouts, Frontmatter, Links.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `parseEdgesFromCallouts.ts` | Parst `>> [!edge] type` Callouts, Targets |
| `parseFrontmatter.ts` | Frontmatter-Referenz |
| `parseRelLinks.ts` | Relative Links in Sections |
| `types.ts` | Parser-Typen |
---
## Edge-Callout-Format
```markdown
>> [!edge] resulted_in
>> [[#^impact]]
>> [[#^learning]]
```
---
## Siehe auch
- [mapping/sectionParser](../mapping.md) Section-Parsing

38
docs/modules/ui.md Normal file
View File

@ -0,0 +1,38 @@
# Modul: UI
> **Pfad:** `src/ui/`
> **Zielgruppe:** Entwickler
---
## Übersicht
UI-Komponenten: Modals, Settings Tab, Entity Picker, Markdown Toolbar.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `MindnetSettingTab.ts` | Plugin-Einstellungen |
| `InterviewWizardModal.ts` | Interview-Wizard |
| `ProfileSelectionModal.ts` | Profil-Auswahl |
| `EntityPickerModal.ts` | Note/Ordner-Auswahl |
| `ChainWorkbenchModal.ts` | Chain Workbench |
| `VaultTriageScanModal.ts` | Vault-Triage-Scan |
| `EdgeTypeChooserModal.ts` | Edge-Type-Auswahl |
| `AdoptNoteModal.ts` | Note-Adoption |
| `LinkPromptModal.ts` | Link-Prompt |
| `LinkTargetPickerModal.ts` | Link-Ziel-Auswahl |
| `ConfirmOverwriteModal.ts` | Überschreibbestätigung |
| `InlineEdgeTypeModal.ts` | Inline Edge-Type-Suggester |
| `FolderTreeModal.ts` | Ordnerbaum |
| `markdownToolbar.ts` | Markdown-Toolbar |
| `MindnetView.ts` | Mindnet-View |
---
## Siehe auch
- [01_Benutzerhandbuch](../01_Benutzerhandbuch.md)

View File

@ -0,0 +1,62 @@
# Modul: Unresolved Link
> **Pfad:** `src/unresolvedLink/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Das Unresolved-Link-Modul behandelt das Abfangen von Klicks auf unaufgelöste Links, Note-Adoption und die Normalisierung von Überschriften für Matching.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `unresolvedLinkHandler.ts` | Haupt-Handler für Link-Clicks |
| `linkHelpers.ts` | Link-Parsing, Heading-Normalisierung, Matching |
| `adoptHelpers.ts` | Note-Adoption-Logik |
---
## Heading-Normalisierung (normalizeHeadingForMatch)
**Zweck:** Überschriften für Vergleich (Inspect Chains, Chain Workbench) auf kanonische Form bringen.
### Regeln
1. **Mit ^Block-ID:** `"Überschrift ^Block"``"Überschrift"` (Block-Suffix entfernen)
2. **Ohne ^:** Rückgabe unverändert (Idempotenz für bereits normalisierte Formen)
### Wichtige Eigenschaft: Idempotenz
`norm(norm(x)) === norm(x)` mehrfache Normalisierung ändert nichts.
### Beispiele
| Input | Output |
|-------|--------|
| `"Lars ist gut ^PersLars"` | `"Lars ist gut"` |
| `"Lars ist gut"` | `"Lars ist gut"` |
| `"Nächster Schritt ^next"` | `"Nächster Schritt"` |
| `"Überschrift ^Block"` | `"Überschrift"` |
### headingsMatch
Vergleicht zwei Überschriften über `normalizeHeadingForMatch` beide müssen nach Normalisierung identisch sein.
---
## Obsidian-Verhalten
- **Manuell verlinkt:** Obsidian erzeugt `[[Titel#Überschrift Block]]` (ohne `^`)
- **Empfehlung:** Für zuverlässiges Matching Links mit `^` verwenden: `[[Note#Überschrift ^Block]]`
---
## Siehe auch
- [12_Heading_Block_Link_Recommendation](../12_Heading_Block_Link_Recommendation.md)
- [Analysis-Modul](analysis.md) Nutzung von headingsMatch

View File

@ -0,0 +1,32 @@
# Modul: Vocabulary
> **Pfad:** `src/vocab/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Edge Vocabulary Loading, Normalisierung (Alias → Canonical), Inverse Edge Resolution.
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `Vocabulary.ts` | Lookup-Methoden |
| `VocabularyLoader.ts` | Lädt edge_vocabulary.md |
| `parseEdgeVocabulary.ts` | Parst Markdown zu EdgeVocabulary |
---
## Format
Markdown mit Tabellen oder Callouts. Kanonische Typen, Inverse, Aliases.
---
## Siehe auch
- [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md) edge_vocabulary

61
docs/modules/workbench.md Normal file
View File

@ -0,0 +1,61 @@
# Modul: Workbench
> **Pfad:** `src/workbench/`
> **Zielgruppe:** Entwickler
---
## Übersicht
Das Workbench-Modul implementiert die **Chain Workbench** eine UI zur Analyse von Ketten und zur Bearbeitung von Lücken (Todos, Actions).
---
## Komponenten
| Datei | Beschreibung |
|-------|--------------|
| `ChainWorkbenchModal.ts` | Haupt-Modal (in ui/) |
| `workbenchBuilder.ts` | Baut Workbench-Daten aus Template Matches |
| `todoGenerator.ts` | Erzeugt Todos (missing_slot, missing_link, weak_roles) |
| `statusCalculator.ts` | Berechnet Status (complete, near_complete, partial, weak) |
| `zoneDetector.ts` | Erkennt Zonen (note_links, candidates_only) |
| `createSectionAction.ts` | Aktion: Sektion erstellen |
| `linkExistingAction.ts` | Aktion: Bestehenden Knoten verlinken |
| `insertEdgeIntoSectionContent.ts` | Edge in Section-Content einfügen |
| `interviewOrchestration.ts` | Orchestriert Interview bei Create-Actions |
---
## Vault Triage Scan
- **Command:** `mindnet-vault-triage-scan`
- **Modal:** VaultTriageScanModal
- **Sortierung:** near_complete zuerst, dann nach Score absteigend
---
## Status-Werte
| Status | Bedingung |
|--------|-----------|
| `complete` | Slots + Links vollständig |
| `near_complete` | 12 Links fehlen oder 1 Slot fehlt |
| `partial` | Mehrere Lücken |
| `weak` | Nur structural/temporal Roles, keine causal Roles |
---
## Fix Actions
- **createMissingNote:** Skeleton, Create+Open, Create+Wizard
- **createMissingHeading:** Neues Heading mit konfigurierbarem Level
- **promoteCandidate:** Candidate als echten Slot übernehmen
---
## Siehe auch
- [09_Workbench_Analysis_Basis](../09_Workbench_Analysis_Basis.md)
- [10_Workbench_Findings_Integration](../10_Workbench_Findings_Integration.md)
- [Analysis-Modul](analysis.md) Template Matching, Scoring

View File

@ -102,6 +102,8 @@ export interface HeadingWithType {
heading: string;
sectionType: string | null;
level: number;
/** Block-ID from `## Title ^id`; null if the heading has none. */
blockId: string | null;
}
export async function getHeadingsWithSectionTypes(
@ -115,10 +117,11 @@ export async function getHeadingsWithSectionTypes(
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line === undefined) continue;
const match = line.match(/^(#{1,6})\s+(.+?)(?:\s*\^[\w-]+)?\s*$/);
const match = line.match(/^(#{1,6})\s+(.+?)(?:\s*\^([a-zA-Z0-9_-]+))?\s*$/);
if (match && match[1] && match[2]) {
const level = match[1].length;
const heading = match[2].trim();
const blockId = match[3] ?? null;
let sectionType: string | null = null;
for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
const nextLine = lines[j];
@ -129,7 +132,7 @@ export async function getHeadingsWithSectionTypes(
break;
}
}
result.push({ heading, sectionType, level });
result.push({ heading, sectionType, level, blockId });
}
}
return result;

View File

@ -383,6 +383,14 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
},
});
this.addCommand({
id: "mindnet-start-interview-current-note",
name: "Mindnet: Interview für aktuelle Note starten",
callback: async () => {
await this.startInterviewForActiveFile();
},
});
this.addCommand({
id: "mindnet-create-note-from-profile",
name: "Mindnet: Create note from profile",
@ -852,7 +860,9 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
async (wizardResult: WizardResult) => {
new Notice("Interview saved and changes applied");
},
this // Pass plugin instance for graph schema loading
this, // Pass plugin instance for graph schema loading
undefined,
!isUnresolvedClick
);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
@ -869,6 +879,66 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
return this.createNoteFromProfileAndOpen(result, result.title);
}
/** Start interview wizard on the open note (profile from frontmatter or picker). */
private async startInterviewForActiveFile(): Promise<void> {
const file = this.app.workspace.getActiveFile();
if (!file || file.extension !== "md") {
new Notice("Bitte eine Markdown-Notiz öffnen");
return;
}
const config = await this.ensureInterviewConfigLoaded();
if (!config) {
return;
}
const content = await this.app.vault.read(file);
const profileKeyMatch = content.match(/^interview_profile\s*:\s*(.+)$/m);
let profileKey = profileKeyMatch?.[1]?.trim() ?? "";
if (
(profileKey.startsWith('"') && profileKey.endsWith('"')) ||
(profileKey.startsWith("'") && profileKey.endsWith("'"))
) {
profileKey = profileKey.slice(1, -1);
}
const profile = profileKey
? config.profiles.find((p) => p.key === profileKey)
: undefined;
const openWizard = (chosen: import("./interview/types").InterviewProfile) => {
new InterviewWizardModal(
this.app,
chosen,
file,
content,
async () => {
new Notice("Interview completed and changes applied");
},
async () => {
new Notice("Interview saved and changes applied");
},
this.settings,
this
).open();
};
if (profile) {
openWizard(profile);
return;
}
new ProfileSelectionModal(
this.app,
config,
async (result) => {
openWizard(result.profile);
},
file.basename,
file.parent?.path ?? ""
).open();
}
/**
* Register unresolved link handlers for Reading View and Live Preview.
*/

View File

@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { normalizeLinkTarget, normalizeHeadingForMatch, headingsMatch, isUnresolvedLink, waitForFileModify, parseWikilinkAtPosition } from "../../unresolvedLink/linkHelpers";
import { normalizeLinkTarget, normalizeHeadingForMatch, headingsMatch, isUnresolvedLink, waitForFileModify, parseWikilinkAtPosition, parseHeadingWithBlockId, formatStableWikilinkTarget, formatStableWikilink } from "../../unresolvedLink/linkHelpers";
import { App, TFile } from "obsidian";
describe("normalizeLinkTarget", () => {
@ -59,6 +59,64 @@ describe("normalizeHeadingForMatch", () => {
});
});
describe("parseHeadingWithBlockId", () => {
it("splits heading and block-id", () => {
expect(parseHeadingWithBlockId("Nächster Schritt ^next")).toEqual({
heading: "Nächster Schritt",
blockId: "next",
});
});
it("returns heading only when no block-id", () => {
expect(parseHeadingWithBlockId("Nächster Schritt")).toEqual({
heading: "Nächster Schritt",
blockId: null,
});
});
});
describe("formatStableWikilinkTarget", () => {
it("writes intra-note block ref", () => {
expect(formatStableWikilinkTarget({ blockId: "sit" })).toBe("#^sit");
});
it("writes cross-note heading + block (vacation-stable form)", () => {
expect(
formatStableWikilinkTarget({
basename: "Geburt",
heading: "Nächster Schritt",
blockId: "next",
})
).toBe("Geburt#Nächster Schritt ^next");
});
it("parses block-id out of heading if passed combined", () => {
expect(
formatStableWikilinkTarget({
basename: "Geburt",
heading: "Nächster Schritt ^next",
})
).toBe("Geburt#Nächster Schritt ^next");
});
it("falls back to heading-only when no block-id", () => {
expect(
formatStableWikilinkTarget({
basename: "Geburt",
heading: "Nächster Schritt",
})
).toBe("Geburt#Nächster Schritt");
});
it("writes note-only link", () => {
expect(formatStableWikilinkTarget({ basename: "Geburt" })).toBe("Geburt");
});
it("wraps in brackets", () => {
expect(formatStableWikilink({ basename: "Geburt", blockId: "sit" })).toBe("[[Geburt#^sit]]");
});
});
describe("headingsMatch", () => {
it("matches same heading", () => {
expect(headingsMatch("Überschrift", "Überschrift")).toBe(true);

View File

@ -1,7 +1,7 @@
/**
* Modal zur Auswahl des Link-Ziels: Ganze Note oder ein Abschnitt (Überschrift inkl. Sektionstyp).
* Wird in der Interview-Toolbar nach der Note-Auswahl angezeigt.
* Speichert Link als [[Note]] oder [[Note#Abschnitt]].
* Speichert Link als [[Note]], [[Note#Abschnitt ^block]] oder [[Note#Abschnitt]].
*/
import { App, Modal, TFile } from "obsidian";
@ -10,9 +10,10 @@ import {
resolveTargetTypeForNoteLink,
type HeadingWithType,
} from "../interview/targetTypeResolver";
import { formatStableWikilinkTarget } from "../unresolvedLink/linkHelpers";
export interface LinkTargetPick {
linkTarget: string; // "Note" oder "Note#Abschnitt"
linkTarget: string; // "Note", "Note#Abschnitt ^block" oder "Note#Abschnitt"
targetType: string | null;
}
@ -102,7 +103,11 @@ export class LinkTargetPickerModal extends Modal {
.createEl("span", { text: ` (Sektion: ${h.sectionType})`, cls: "target-type" })
.style.color = "var(--text-muted)";
}
const linkTarget = `${this.basename}#${h.heading}`;
const linkTarget = formatStableWikilinkTarget({
basename: this.basename,
heading: h.heading,
blockId: h.blockId,
});
headingBtn.onclick = () => {
if (this.resolve) {
this.resolve({ linkTarget, targetType: h.sectionType });

View File

@ -465,7 +465,7 @@ export class MindnetSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName("Auto-start interview on create")
.setDesc(
"Startet automatisch den Interview-Wizard, wenn eine neue Notiz über ein Profil erstellt wird."
"Zusätzlich zum Command „Create note from profile“ (der den Wizard immer startet): Wizard auch bei anderen Create-Wegen automatisch öffnen."
)
.addToggle((toggle) =>
toggle

View File

@ -31,6 +31,71 @@ export function normalizeHeadingForMatch(heading: string | null): string | null
return s || null;
}
/**
* Split heading text into title and optional trailing block-id.
* "Nächster Schritt ^next" { heading: "Nächster Schritt", blockId: "next" }
* "Nächster Schritt" { heading: "Nächster Schritt", blockId: null }
* "^sit" / "#^sit" are not headings; use formatStableWikilinkTarget for those.
*/
export function parseHeadingWithBlockId(raw: string | null | undefined): {
heading: string | null;
blockId: string | null;
} {
if (raw == null) return { heading: null, blockId: null };
const s = raw.trim();
if (!s) return { heading: null, blockId: null };
const match = s.match(/^(.*?)(?:\s*\^([a-zA-Z0-9_-]+))\s*$/);
if (match && match[2]) {
const heading = (match[1] ?? "").trim();
return { heading: heading || null, blockId: match[2] };
}
return { heading: s, blockId: null };
}
export interface StableLinkParts {
/** Note basename. Empty/null = intra-note link. */
basename?: string | null;
heading?: string | null;
blockId?: string | null;
}
/**
* Stable wikilink *target* (without [[ ]]). Prefer this for every write path.
* Intra-note + blockId #^block
* Cross-note + heading + blockId Note#Heading ^block
* Cross-note + blockId only Note#^block
* Cross-note + heading only Note#Heading
* Note only Note
*/
export function formatStableWikilinkTarget(parts: StableLinkParts): string {
const basename = (parts.basename ?? "").trim();
let heading = parts.heading?.trim() || null;
let blockId = parts.blockId?.trim() || null;
if (heading && !blockId) {
const parsed = parseHeadingWithBlockId(heading);
heading = parsed.heading;
blockId = parsed.blockId;
} else if (heading && blockId) {
heading = parseHeadingWithBlockId(heading).heading ?? heading.replace(/\s*\^[a-zA-Z0-9_-]+\s*$/, "").trim();
}
if (!basename) {
if (blockId) return `#^${blockId}`;
if (heading) return `#${heading}`;
return "";
}
if (heading && blockId) return `${basename}#${heading} ^${blockId}`;
if (blockId) return `${basename}#^${blockId}`;
if (heading) return `${basename}#${heading}`;
return basename;
}
/** Same as formatStableWikilinkTarget, wrapped in [[ ]]. */
export function formatStableWikilink(parts: StableLinkParts): string {
const target = formatStableWikilinkTarget(parts);
return target ? `[[${target}]]` : "";
}
/**
* Compare two headings for equality using normalized form (block-id suffix and trailing word stripped).
* Use for Inspect Chains / Chain Workbench matching only.

View File

@ -49,12 +49,13 @@ export async function startWizardAfterCreate(
onWizardComplete: (result: any) => void,
onWizardSave: (result: any) => void,
pluginInstance?: { ensureGraphSchemaLoaded?: () => Promise<import("../mapping/graphSchema").GraphSchema | null> },
initialPendingEdgeAssignments?: import("../interview/wizardState").PendingEdgeAssignment[]
initialPendingEdgeAssignments?: import("../interview/wizardState").PendingEdgeAssignment[],
forceStart = false
): Promise<void> {
// Determine if wizard should start
const shouldStartInterview = isUnresolvedClick
const shouldStartInterview = forceStart || (isUnresolvedClick
? (settings.autoStartOnUnresolvedClick || settings.autoStartInterviewOnCreate)
: settings.autoStartInterviewOnCreate;
: settings.autoStartInterviewOnCreate);
if (!shouldStartInterview) {
if (settings.debugLogging) {

View File

@ -14,6 +14,8 @@ import { EntityPickerModal } from "../ui/EntityPickerModal";
import { NoteIndex } from "../entityPicker/noteIndex";
import { detectZoneFromContent } from "./zoneDetector";
import { removeBlanksAndInsert } from "./edgeInsertHelper";
import { formatStableWikilinkTarget } from "../unresolvedLink/linkHelpers";
import { slugify } from "../interview/slugify";
/** Common section/node types for dropdown when slot has no allowed_node_types. */
const FALLBACK_SECTION_TYPES = [
@ -130,9 +132,10 @@ export async function createSectionInNote(
sectionContent += `> [!${wrapperType}]${foldMarker}${wrapperTitle ? ` ${wrapperTitle}` : ""}\n`;
for (const edge of result.initialEdges) {
const targetLink = edge.targetHeading
? `${edge.targetNote}#${edge.targetHeading}`
: edge.targetNote;
const targetLink = formatStableWikilinkTarget({
basename: edge.targetNote,
heading: edge.targetHeading,
});
sectionContent += `>> [!edge] ${edge.edgeType}\n>> [[${targetLink}]]\n`;
}
@ -159,13 +162,11 @@ export async function createSectionInNote(
// Gegenkanten in den anderen Notes einfügen (Vorwärts- bzw. Rückwärtskante)
const sourcePath = result.targetFilePath.replace(/\.md$/, "");
const sourceFragment =
result.blockId && result.heading
? `${result.heading} ^${result.blockId}`
: result.blockId
? `^${result.blockId}`
: result.heading ?? "";
const sourceLink = sourceFragment ? `${sourcePath}#${sourceFragment}` : sourcePath;
const sourceLink = formatStableWikilinkTarget({
basename: sourcePath,
heading: result.heading,
blockId: result.blockId,
});
for (const edge of result.initialEdges) {
const otherPathRaw = edge.targetNote.replace(/\.md$/, "");
@ -553,10 +554,10 @@ async function showCreateSectionModal(
});
});
// Block ID (optional)
// Block ID — auto from heading if left empty (stable links for later ingest)
new Setting(contentEl)
.setName("Block-ID (optional)")
.setDesc("z. B. learning, insight-1")
.setName("Block-ID")
.setDesc("Pflicht für stabile Links. Leer = aus der Überschrift erzeugt.")
.addText((text) => {
text.setValue("").onChange((value) => {
this.blockId = value.trim();
@ -616,6 +617,12 @@ async function showCreateSectionModal(
new Notice("Bitte eine Ziel-Note wählen");
return;
}
const heading = this.heading.trim();
const blockId = this.blockId || slugify(heading);
if (!blockId) {
new Notice("Block-ID fehlt und konnte nicht aus der Überschrift erzeugt werden");
return;
}
this.initialEdges = requiredEdges.map((e, i) => ({
edgeType: this.chosenEdgeTypes[i] ?? e.suggestedAlias ?? e.edgeType,
targetNote: e.targetNote,
@ -625,8 +632,8 @@ async function showCreateSectionModal(
}));
this.result = {
targetFilePath: this.targetFilePath,
heading: this.heading.trim(),
blockId: this.blockId || null,
heading,
blockId,
sectionType: this.sectionType || null,
sectionBody: this.sectionBody,
initialEdges: this.initialEdges,

View File

@ -184,7 +184,8 @@ export async function createNoteViaInterview(
new Notice("Wizard saved");
},
pluginInstance,
pendingEdgeAssignments // Pass initial edge assignments to wizard
pendingEdgeAssignments,
true
);
}

View File

@ -2,12 +2,14 @@
* Link existing action for missing_slot todos.
*/
import { Notice } from "obsidian";
import type { App, Editor, TFile } from "obsidian";
import { Notice, TFile } from "obsidian";
import type { App, Editor } from "obsidian";
import type { MissingSlotTodo } from "./types";
import { EntityPickerModal } from "../ui/EntityPickerModal";
import { LinkTargetPickerModal } from "../ui/LinkTargetPickerModal";
import { NoteIndex } from "../entityPicker/noteIndex";
import { findSection } from "./zoneDetector";
import { formatStableWikilink } from "../unresolvedLink/linkHelpers";
/**
* Link existing note/section to fill missing slot.
@ -56,7 +58,15 @@ export async function linkExisting(
// This is a placeholder - the real implementation would need to update the match
const targetNote = selectedNote.basename.replace(/\.md$/, "");
const linkText = `[[${targetNote}]]`;
let linkText = formatStableWikilink({ basename: targetNote });
const pickedFile = app.vault.getAbstractFileByPath(selectedNote.path);
if (pickedFile instanceof TFile) {
const picker = new LinkTargetPickerModal(app, pickedFile, targetNote, file.path);
const pick = await picker.show();
if (pick?.linkTarget) {
linkText = `[[${pick.linkTarget}]]`;
}
}
// Insert link at cursor position
const cursor = editor.getCursor();

View File

@ -14,7 +14,7 @@ import type { Vocabulary } from "../vocab/Vocabulary";
import type { EdgeVocabulary } from "../vocab/types";
import type { MindnetSettings } from "../settings";
import { computeSectionContentAfterInsertEdge } from "./insertEdgeIntoSectionContent";
import { headingsMatch } from "../unresolvedLink/linkHelpers";
import { formatStableWikilinkTarget, headingsMatch } from "../unresolvedLink/linkHelpers";
/**
* Insert edge forward (from source to target).
@ -68,9 +68,10 @@ export async function insertEdgeForward(
// Build target link (extract basename from file path)
const targetBasename = todo.toNodeRef.file.replace(/\.md$/, "").split("/").pop() || todo.toNodeRef.file;
const targetLink = todo.toNodeRef.heading
? `${targetBasename}#${todo.toNodeRef.heading}`
: targetBasename;
const targetLink = formatStableWikilinkTarget({
basename: targetBasename,
heading: todo.toNodeRef.heading,
});
if (debugLogging) {
console.log("[insertEdgeForward] Target link:", targetLink);
@ -486,9 +487,10 @@ async function createInverseEdge(
// Build source link (for inverse edge, source is the original target)
const sourceBasename = todo.fromNodeRef.file.replace(/\.md$/, "").split("/").pop() || todo.fromNodeRef.file;
const sourceLink = todo.fromNodeRef.heading
? `${sourceBasename}#${todo.fromNodeRef.heading}`
: sourceBasename;
const sourceLink = formatStableWikilinkTarget({
basename: sourceBasename,
heading: todo.fromNodeRef.heading,
});
// Always ask user to select target section (or whole note)
// Open target file in editor first
@ -973,9 +975,10 @@ export async function promoteCandidate(
// Find candidate edge in zone
const candidateEdge = todo.candidateEdge;
const edgeType = candidateEdge.rawEdgeType;
const targetLink = candidateEdge.to.heading
? `[[${candidateEdge.to.file}#${candidateEdge.to.heading}]]`
: `[[${candidateEdge.to.file}]]`;
const targetLink = formatStableWikilinkTarget({
basename: candidateEdge.to.file,
heading: candidateEdge.to.heading,
});
// Determine source section (from evidence or current context)
const sourceSectionHeading = candidateEdge.evidence.sectionHeading;