Enhance interview configuration and rendering for WP-26 integration
- Expanded the interview configuration YAML to include new profiles for experience and insight, with detailed steps for capturing user input. - Updated the parsing logic to support new input types, including select options, enhancing user interaction during interviews. - Improved the rendering logic to ensure correct handling of section edges and types, aligning with the updated configuration structure. - Enhanced tests to validate the new configurations and rendering behavior, ensuring robustness in the interview process.
This commit is contained in:
parent
8186ca5ce0
commit
054cfcf82d
|
|
@ -23,140 +23,20 @@ ui_defaults:
|
|||
|
||||
profiles:
|
||||
|
||||
- key: experience_cluster
|
||||
group: experience
|
||||
label: "Experience – Cluster"
|
||||
# ---------------------------------------------------------------------------
|
||||
# EXPERIENCE
|
||||
# ---------------------------------------------------------------------------
|
||||
- key: experience_basic
|
||||
group: history
|
||||
label: "Experience – Basis"
|
||||
note_type: experience
|
||||
defaults:
|
||||
status: active
|
||||
folder: "03_experience"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Hub Titel"
|
||||
required: true
|
||||
# Einzeiliges Feld
|
||||
- id: subtitle
|
||||
kind: capture_text_line
|
||||
label: "Untertitel"
|
||||
heading_level:
|
||||
enabled: true
|
||||
default: 1
|
||||
prompt: "Kurzer Untertitel"
|
||||
- id: intro
|
||||
kind: capture_text
|
||||
section: "## Einleitung"
|
||||
label: "Einleitung"
|
||||
prompt: "Beschreibe die Einleitung"
|
||||
- id: items
|
||||
kind: loop
|
||||
label: "Erlebnisse"
|
||||
item_label: "Erlebnis"
|
||||
min_items: 1
|
||||
steps:
|
||||
- id: item_Headline
|
||||
kind: capture_text_line
|
||||
label: "Überschrift"
|
||||
required: false
|
||||
heading_level:
|
||||
enabled: true
|
||||
default: 2
|
||||
prompt: "Nennen kurz die Gruppenüberschrift"
|
||||
- id: level2
|
||||
label: "Listeneinträge zusammenstellen"
|
||||
kind: loop
|
||||
steps:
|
||||
- id: item_text
|
||||
kind: capture_text
|
||||
section: ""
|
||||
label: "Liste"
|
||||
required: true
|
||||
prompt: "Stelle eine Liste von Erlebnisse zusammen"
|
||||
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks: [lint_current_note]
|
||||
|
||||
|
||||
- key: experience_cluster_2
|
||||
group: experience
|
||||
label: "Experience – Cluster2"
|
||||
note_type: experience
|
||||
defaults:
|
||||
status: active
|
||||
chunking_profile: timeline
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Cluster Titel"
|
||||
required: true
|
||||
input:
|
||||
kind: text_line
|
||||
|
||||
# Gruppen = Überschrift + viele Listeneinträge
|
||||
- id: groups
|
||||
kind: loop
|
||||
label: "Erlebnis-Gruppen"
|
||||
ui:
|
||||
mode: subwizard # pro Gruppe eigener Flow
|
||||
commit: on_next # kein Add-Item-Zwang
|
||||
allow_edit: true
|
||||
allow_delete: true
|
||||
allow_reorder: true
|
||||
show_item_overview: true # Übersicht, aber editierbar
|
||||
output:
|
||||
join: "\n" # Gruppen durch Leerzeile trennen
|
||||
steps:
|
||||
- id: group_heading
|
||||
kind: heading
|
||||
label: "Überschrift"
|
||||
required: true
|
||||
default_level: 2
|
||||
allow_level_change: true
|
||||
output:
|
||||
template: "{hashes} {text}\n"
|
||||
|
||||
- id: entries
|
||||
kind: loop
|
||||
label: "Einträge"
|
||||
ui:
|
||||
mode: subwizard
|
||||
commit: on_next
|
||||
allow_edit: true
|
||||
allow_delete: true
|
||||
allow_reorder: true
|
||||
show_item_overview: true
|
||||
output:
|
||||
join: "" # Einträge direkt unter Überschrift
|
||||
steps:
|
||||
- id: entry
|
||||
kind: text_line
|
||||
label: "Listeneintrag"
|
||||
required: true
|
||||
output:
|
||||
template: "- {text}\n"
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: experience_single
|
||||
group: experience
|
||||
label: "Experience – Einzelereignis"
|
||||
note_type: experience
|
||||
defaults:
|
||||
status: active
|
||||
chunking_profile: timeline
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
|
|
@ -164,26 +44,63 @@ profiles:
|
|||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: context
|
||||
- id: significance
|
||||
kind: capture_frontmatter
|
||||
field: retriever_weight
|
||||
label: "Bedeutung"
|
||||
required: false
|
||||
input:
|
||||
kind: select
|
||||
options:
|
||||
- label: "Normal (1.0)"
|
||||
value: 1.0
|
||||
- label: "Signifikant / prägend (1.1)"
|
||||
value: 1.1
|
||||
|
||||
- id: situation
|
||||
kind: capture_text
|
||||
section: "## 📖 Kontext"
|
||||
label: "Kontext"
|
||||
section: "## Situation (Was ist passiert?)"
|
||||
label: "Situation"
|
||||
required: true
|
||||
prompt: "In welchem Rahmen ist es passiert?"
|
||||
prompt: "Was ist passiert? Wer war beteiligt? Was war der Kontext?"
|
||||
section_type: experience
|
||||
generate_block_id: true
|
||||
|
||||
- id: trigger
|
||||
- id: reaction
|
||||
kind: capture_text
|
||||
section: "## ⚡ Auslöser"
|
||||
label: "Auslöser"
|
||||
required: false
|
||||
prompt: "Was hat es ausgelöst?"
|
||||
section: "## Meine Reaktion (Was habe ich getan?)"
|
||||
label: "Reaktion"
|
||||
required: true
|
||||
prompt: "Was hast du konkret getan/gesagt/unterlassen?"
|
||||
section_type: experience
|
||||
generate_block_id: true
|
||||
|
||||
- id: transformation
|
||||
- id: impact
|
||||
kind: capture_text
|
||||
section: "## 🧠 Innere Transformation"
|
||||
label: "Innere Transformation"
|
||||
section: "## Ergebnis & Auswirkung"
|
||||
label: "Auswirkung"
|
||||
required: false
|
||||
prompt: "Was hat sich innerlich verändert?"
|
||||
prompt: "Welche Folgen hatte es (kurzfristig/langfristig)?"
|
||||
section_type: state
|
||||
generate_block_id: true
|
||||
|
||||
- id: learning
|
||||
kind: capture_text
|
||||
section: "## Reflexion & Learning (Was lerne ich daraus?)"
|
||||
label: "Learning"
|
||||
required: false
|
||||
prompt: "Welche Erkenntnis oder Regel ergibt sich daraus?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- id: next
|
||||
kind: capture_text
|
||||
section: "## Nächster Schritt"
|
||||
label: "Nächster Schritt"
|
||||
required: false
|
||||
prompt: "Was folgt daraus ganz konkret?"
|
||||
section_type: decision
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
|
|
@ -194,11 +111,16 @@ profiles:
|
|||
- missing_frontmatter_id
|
||||
|
||||
- key: experience_hub
|
||||
group: experience
|
||||
group: history
|
||||
label: "Experience – Hub"
|
||||
note_type: experience
|
||||
defaults:
|
||||
status: active
|
||||
folder: "03_experience"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.2
|
||||
edging:
|
||||
mode: post_run
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
|
|
@ -206,34 +128,564 @@ profiles:
|
|||
label: "Hub Titel"
|
||||
required: true
|
||||
|
||||
- id: purpose
|
||||
kind: capture_text
|
||||
section: "## Zweck / Klammer"
|
||||
label: "Zweck"
|
||||
required: false
|
||||
prompt: "Wozu ist dieser Hub da? Welche Art von Erlebnissen sammelt er?"
|
||||
section_type: experience
|
||||
generate_block_id: true
|
||||
|
||||
- id: items
|
||||
kind: loop
|
||||
label: "Erlebnisse"
|
||||
item_label: "Erlebnis"
|
||||
label: "Einträge"
|
||||
item_label: "Eintrag"
|
||||
min_items: 1
|
||||
steps:
|
||||
- id: item_text
|
||||
kind: capture_text
|
||||
section: "## 🧩 Erlebnisse"
|
||||
label: "Erlebnis"
|
||||
- id: item
|
||||
kind: capture_text_line
|
||||
label: "Erlebnis (Kurzform)"
|
||||
required: true
|
||||
prompt: "Beschreibe ein Erlebnis (kurz)."
|
||||
prompt: "Kurzbeschreibung + ggf. Link auf Detail-Note"
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_frontmatter_id
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# INSIGHT
|
||||
# ---------------------------------------------------------------------------
|
||||
- key: insight_basic
|
||||
group: knowledge
|
||||
label: "Insight – Basis"
|
||||
note_type: insight
|
||||
defaults:
|
||||
status: active
|
||||
folder: "04_insight"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: significance
|
||||
kind: capture_frontmatter
|
||||
field: retriever_weight
|
||||
label: "Bedeutung"
|
||||
required: false
|
||||
input:
|
||||
kind: select
|
||||
options:
|
||||
- label: "Normal (1.0)"
|
||||
value: 1.0
|
||||
- label: "Signifikant (1.1)"
|
||||
value: 1.1
|
||||
|
||||
- id: observation
|
||||
kind: capture_text
|
||||
section: "## Beobachtung (Was sehe ich?)"
|
||||
label: "Beobachtung"
|
||||
required: true
|
||||
prompt: "Welche konkrete Beobachtung ist der Ausgangspunkt?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- id: interpretation
|
||||
kind: capture_text
|
||||
section: "## Interpretation (Was bedeutet das?)"
|
||||
label: "Interpretation"
|
||||
required: true
|
||||
prompt: "Welche Erklärung/Schlussfolgerung ziehst du daraus?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- id: need
|
||||
kind: capture_text
|
||||
section: "## Bedürfnis (Was steckt dahinter?)"
|
||||
label: "Bedürfnis"
|
||||
required: false
|
||||
prompt: "Welches Bedürfnis/Anliegen wird sichtbar?"
|
||||
section_type: need
|
||||
generate_block_id: true
|
||||
|
||||
- id: recommendation
|
||||
kind: capture_text
|
||||
section: "## Handlungsempfehlung"
|
||||
label: "Handlungsempfehlung"
|
||||
required: false
|
||||
prompt: "Welche konkrete Empfehlung ergibt sich?"
|
||||
section_type: decision
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks: [lint_current_note]
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: insight_hub
|
||||
group: knowledge
|
||||
label: "Insight – Hub"
|
||||
note_type: insight
|
||||
defaults:
|
||||
status: active
|
||||
folder: "04_insight"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.2
|
||||
edging:
|
||||
mode: post_run
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Hub Titel"
|
||||
required: true
|
||||
|
||||
- id: scope
|
||||
kind: capture_text
|
||||
section: "## Scope"
|
||||
label: "Scope"
|
||||
required: false
|
||||
prompt: "Welche Art von Insights werden hier gesammelt?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- key: principle
|
||||
group: principle
|
||||
label: "Principle"
|
||||
- id: items
|
||||
kind: loop
|
||||
label: "Einträge"
|
||||
item_label: "Eintrag"
|
||||
min_items: 1
|
||||
steps:
|
||||
- id: item
|
||||
kind: capture_text_line
|
||||
label: "Insight (Kurzform)"
|
||||
required: true
|
||||
prompt: "Kurzform + ggf. Link auf Detail-Note"
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_frontmatter_id
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DECISION
|
||||
# ---------------------------------------------------------------------------
|
||||
- key: decision_basic
|
||||
group: action
|
||||
label: "Decision – Basis"
|
||||
note_type: decision
|
||||
defaults:
|
||||
status: active
|
||||
folder: "05_decision"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: significance
|
||||
kind: capture_frontmatter
|
||||
field: retriever_weight
|
||||
label: "Bedeutung"
|
||||
required: false
|
||||
input:
|
||||
kind: select
|
||||
options:
|
||||
- label: "Normal (1.0)"
|
||||
value: 1.0
|
||||
- label: "Signifikant (1.1)"
|
||||
value: 1.1
|
||||
|
||||
- id: context
|
||||
kind: capture_text
|
||||
section: "## Kontext & Problemstellung"
|
||||
label: "Kontext"
|
||||
required: true
|
||||
prompt: "Was ist die Situation? Welches Problem/Tradeoff muss gelöst werden?"
|
||||
section_type: decision
|
||||
generate_block_id: true
|
||||
|
||||
- id: options
|
||||
kind: loop
|
||||
label: "Optionen"
|
||||
item_label: "Option"
|
||||
min_items: 1
|
||||
steps:
|
||||
- id: option
|
||||
kind: capture_text
|
||||
section: ""
|
||||
label: "Option"
|
||||
required: true
|
||||
prompt: "Beschreibe eine Option inkl. Vor-/Nachteile (kurz)."
|
||||
|
||||
- id: rationale
|
||||
kind: capture_text
|
||||
section: "## Begründung (Werte/Prinzipien/Einsichten)"
|
||||
label: "Begründung"
|
||||
required: true
|
||||
prompt: "Welche Werte/Prinzipien/Erfahrungen/Einsichten begründen die Wahl?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- id: decision
|
||||
kind: capture_text
|
||||
section: "## Entscheidung"
|
||||
label: "Entscheidung"
|
||||
required: true
|
||||
prompt: "Welche Entscheidung triffst du konkret?"
|
||||
section_type: decision
|
||||
generate_block_id: true
|
||||
|
||||
- id: risks
|
||||
kind: capture_text
|
||||
section: "## Risiken & Tradeoffs"
|
||||
label: "Risiken"
|
||||
required: false
|
||||
prompt: "Welche Risiken nimmst du bewusst in Kauf? Welche Gegenmaßnahmen?"
|
||||
section_type: risk
|
||||
generate_block_id: true
|
||||
|
||||
- id: execution
|
||||
kind: capture_text
|
||||
section: "## Umsetzung / Nächste Schritte"
|
||||
label: "Umsetzung"
|
||||
required: false
|
||||
prompt: "Was sind die nächsten konkreten Schritte?"
|
||||
section_type: task
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PROJECT / GOAL / TASK
|
||||
# ---------------------------------------------------------------------------
|
||||
- key: project_basic
|
||||
group: action
|
||||
label: "Project – Basis"
|
||||
note_type: project
|
||||
defaults:
|
||||
status: active
|
||||
folder: "02_project"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: mission
|
||||
kind: capture_text
|
||||
section: "## Mission & Zielsetzung"
|
||||
label: "Mission"
|
||||
required: true
|
||||
prompt: "Wozu gibt es dieses Projekt? Was ist der Zielzustand?"
|
||||
section_type: goal
|
||||
generate_block_id: true
|
||||
|
||||
- id: status
|
||||
kind: capture_text
|
||||
section: "## Aktueller Status"
|
||||
label: "Status"
|
||||
required: false
|
||||
prompt: "Wo stehst du gerade?"
|
||||
section_type: state
|
||||
generate_block_id: true
|
||||
|
||||
- id: blockers
|
||||
kind: capture_text
|
||||
section: "## Blockaden / Hindernisse"
|
||||
label: "Blockaden"
|
||||
required: false
|
||||
prompt: "Was blockiert Fortschritt? Warum?"
|
||||
section_type: obstacle
|
||||
generate_block_id: true
|
||||
|
||||
- id: next
|
||||
kind: capture_text
|
||||
section: "## Nächste konkrete Schritte"
|
||||
label: "Nächste Schritte"
|
||||
required: true
|
||||
prompt: "Welche 1–3 nächsten Schritte bringen dich voran?"
|
||||
section_type: task
|
||||
generate_block_id: true
|
||||
|
||||
- id: risks
|
||||
kind: capture_text
|
||||
section: "## Risiken"
|
||||
label: "Risiken"
|
||||
required: false
|
||||
prompt: "Welche Risiken gibt es, und was tust du dagegen?"
|
||||
section_type: risk
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: goal_basic
|
||||
group: action
|
||||
label: "Goal – Basis"
|
||||
note_type: goal
|
||||
defaults:
|
||||
status: active
|
||||
folder: "02_goal"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: target
|
||||
kind: capture_text
|
||||
section: "## Zielzustand"
|
||||
label: "Zielzustand"
|
||||
required: true
|
||||
prompt: "Was ist der Zielzustand (klar, überprüfbar)?"
|
||||
section_type: goal
|
||||
generate_block_id: true
|
||||
|
||||
- id: why
|
||||
kind: capture_text
|
||||
section: "## Motivation / Warum"
|
||||
label: "Warum"
|
||||
required: false
|
||||
prompt: "Warum ist es dir wichtig?"
|
||||
section_type: motivation
|
||||
generate_block_id: true
|
||||
|
||||
- id: constraints
|
||||
kind: capture_text
|
||||
section: "## Constraints"
|
||||
label: "Constraints"
|
||||
required: false
|
||||
prompt: "Welche Randbedingungen (Zeit, Energie, Ressourcen) gelten?"
|
||||
section_type: obstacle
|
||||
generate_block_id: true
|
||||
|
||||
- id: next
|
||||
kind: capture_text
|
||||
section: "## Nächster Schritt"
|
||||
label: "Nächster Schritt"
|
||||
required: false
|
||||
prompt: "Was ist der nächste konkrete Schritt?"
|
||||
section_type: task
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: task_basic
|
||||
group: action
|
||||
label: "Task – Basis"
|
||||
note_type: task
|
||||
defaults:
|
||||
status: active
|
||||
folder: "02_task"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: task
|
||||
kind: capture_text
|
||||
section: "## Aufgabe"
|
||||
label: "Aufgabe"
|
||||
required: true
|
||||
prompt: "Was ist zu tun (klar, klein, überprüfbar)?"
|
||||
section_type: task
|
||||
generate_block_id: true
|
||||
|
||||
- id: context
|
||||
kind: capture_text
|
||||
section: "## Kontext"
|
||||
label: "Kontext"
|
||||
required: false
|
||||
prompt: "Zu welchem Projekt/Entscheidung gehört es? Warum jetzt?"
|
||||
section_type: project
|
||||
generate_block_id: true
|
||||
|
||||
- id: dod
|
||||
kind: capture_text
|
||||
section: "## Definition of Done"
|
||||
label: "DoD"
|
||||
required: false
|
||||
prompt: "Woran erkennst du: fertig?"
|
||||
section_type: task
|
||||
generate_block_id: true
|
||||
|
||||
- id: blockers
|
||||
kind: capture_text
|
||||
section: "## Blocker"
|
||||
label: "Blocker"
|
||||
required: false
|
||||
prompt: "Was blockiert?"
|
||||
section_type: obstacle
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# VALUE / PRINCIPLE (Normativer Kern)
|
||||
# ---------------------------------------------------------------------------
|
||||
- key: value_basic
|
||||
group: identity
|
||||
label: "Value – Basis"
|
||||
note_type: value
|
||||
defaults:
|
||||
status: active
|
||||
folder: "01_value"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Titel"
|
||||
required: true
|
||||
|
||||
- id: definition
|
||||
kind: capture_text
|
||||
section: "## Definition"
|
||||
label: "Definition"
|
||||
required: true
|
||||
prompt: "Was bedeutet dieser Wert für dich (in einem Satz + 2–3 Bulletpoints)?"
|
||||
section_type: value
|
||||
generate_block_id: true
|
||||
|
||||
- id: origin
|
||||
kind: capture_text
|
||||
section: "## Herkunft / Warum mir das wichtig ist"
|
||||
label: "Warum"
|
||||
required: false
|
||||
prompt: "Woher kommt das (Erlebnis/Einsicht)?"
|
||||
section_type: experience
|
||||
generate_block_id: true
|
||||
|
||||
- id: principles
|
||||
kind: capture_text
|
||||
section: "## Operationalisierung (Prinzipien)"
|
||||
label: "Prinzipien"
|
||||
required: false
|
||||
prompt: "Welche Prinzipien machen den Wert im Alltag sichtbar?"
|
||||
section_type: principle
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: value_hub
|
||||
group: identity
|
||||
label: "Value – Hub"
|
||||
note_type: value
|
||||
defaults:
|
||||
status: active
|
||||
folder: "01_value"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.2
|
||||
edging:
|
||||
mode: post_run
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
field: title
|
||||
label: "Hub Titel"
|
||||
required: true
|
||||
- id: scope
|
||||
kind: capture_text
|
||||
section: "## Scope"
|
||||
label: "Scope"
|
||||
required: false
|
||||
prompt: "Welche Werte sind hier gebündelt und warum?"
|
||||
section_type: value
|
||||
generate_block_id: true
|
||||
- id: items
|
||||
kind: loop
|
||||
label: "Einträge"
|
||||
item_label: "Wert"
|
||||
min_items: 1
|
||||
steps:
|
||||
- id: item
|
||||
kind: capture_text_line
|
||||
label: "Wert (Kurzform)"
|
||||
required: true
|
||||
prompt: "Kurzform + Link auf Detail-Note"
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_frontmatter_id
|
||||
|
||||
- key: principle_basic
|
||||
group: identity
|
||||
label: "Principle – Basis"
|
||||
note_type: principle
|
||||
defaults:
|
||||
status: stable
|
||||
chunking_profile: principle_dense
|
||||
retriever_weight: 2
|
||||
status: active
|
||||
folder: "01_principle"
|
||||
chunking_profile: structured_smart_edges
|
||||
retriever_weight: 1.0
|
||||
edging:
|
||||
mode: both
|
||||
steps:
|
||||
- id: title
|
||||
kind: capture_frontmatter
|
||||
|
|
@ -243,34 +695,36 @@ profiles:
|
|||
|
||||
- id: statement
|
||||
kind: capture_text
|
||||
section: "## 🧭 Prinzip"
|
||||
section: "## Prinzip"
|
||||
label: "Prinzip"
|
||||
required: true
|
||||
prompt: "Formuliere das Prinzip als klaren Satz."
|
||||
section_type: principle
|
||||
generate_block_id: true
|
||||
|
||||
- id: retriever_weight
|
||||
kind: capture_frontmatter
|
||||
field: retriever_weight
|
||||
label: "Retriever Weight"
|
||||
- id: application
|
||||
kind: capture_text
|
||||
section: "## Anwendung (Entscheidungsregeln)"
|
||||
label: "Anwendung"
|
||||
required: false
|
||||
input:
|
||||
kind: number
|
||||
min: -3
|
||||
max: 3
|
||||
step: 1
|
||||
prompt: "Wie wendest du es konkret an? (Wenn-dann-Regeln, Beispiele)"
|
||||
section_type: decision
|
||||
generate_block_id: true
|
||||
|
||||
- id: llm_refine
|
||||
kind: llm_dialog
|
||||
label: "LLM – Verdichtung (manuell)"
|
||||
mode: manual
|
||||
prompt_template: |
|
||||
Verdichte die bisherigen Inhalte zu 3-5 Bulletpoints.
|
||||
Erfinde keine Fakten.
|
||||
output_target:
|
||||
kind: section_append
|
||||
section: "## 🧠 Verdichtung (LLM Vorschlag)"
|
||||
- id: signals
|
||||
kind: capture_text
|
||||
section: "## Signale / Wächterfragen"
|
||||
label: "Signale"
|
||||
required: false
|
||||
prompt: "Woran erkennst du, dass du dem Prinzip folgst oder davon abweichst?"
|
||||
section_type: insight
|
||||
generate_block_id: true
|
||||
|
||||
- id: review
|
||||
kind: review
|
||||
label: "Review & Apply"
|
||||
checks: [lint_current_note]
|
||||
checks:
|
||||
- lint_current_note
|
||||
- missing_targets
|
||||
- missing_frontmatter_id
|
||||
|
||||
|
|
|
|||
|
|
@ -284,7 +284,33 @@ function parseStep(raw: Record<string, unknown>): InterviewStep | null {
|
|||
if (typeof raw.prompt === "string" && raw.prompt.trim()) {
|
||||
step.prompt = raw.prompt.trim();
|
||||
}
|
||||
|
||||
|
||||
// Parse input (kind: select | number | text_line, options for select)
|
||||
if (raw.input && typeof raw.input === "object") {
|
||||
const input = raw.input as Record<string, unknown>;
|
||||
const inputKind =
|
||||
typeof input.kind === "string" && input.kind.trim()
|
||||
? (input.kind.trim() as "text_line" | "number" | "select")
|
||||
: "text_line";
|
||||
step.input = { kind: inputKind };
|
||||
if (inputKind === "select" && Array.isArray(input.options)) {
|
||||
step.input.options = [];
|
||||
for (const opt of input.options) {
|
||||
if (opt && typeof opt === "object") {
|
||||
const o = opt as Record<string, unknown>;
|
||||
const label = typeof o.label === "string" ? o.label.trim() : "";
|
||||
const value = typeof o.value === "number" ? o.value : typeof o.value === "string" ? o.value : "";
|
||||
if (label !== "" || value !== "") {
|
||||
step.input!.options!.push({ label: label || String(value), value });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof input.min === "number") step.input.min = input.min;
|
||||
if (typeof input.max === "number") step.input.max = input.max;
|
||||
if (typeof input.step === "number") step.input.step = input.step;
|
||||
}
|
||||
|
||||
// Parse output.template
|
||||
if (raw.output && typeof raw.output === "object") {
|
||||
const output = raw.output as Record<string, unknown>;
|
||||
|
|
|
|||
|
|
@ -285,9 +285,9 @@ describe("WP-26 Interview Renderer", () => {
|
|||
|
||||
const result = renderProfileToMarkdown(profile, answers, mockOptions);
|
||||
|
||||
// Prüfe, dass Edges am Ende der Einsicht-Sektion stehen (nach dem Text)
|
||||
const insightSection = result.match(/## Einsicht[\s\S]*?Einsicht-Text\n\n(> \[!edge\][\s\S]*)/)?.[1];
|
||||
// Prüfe, dass Edges am Ende der Einsicht-Sektion stehen (nach dem Text; Abstract-Wrapper nutzt >>)
|
||||
const insightSection = result.match(/## Einsicht[\s\S]*?Einsicht-Text\n\n([\s\S]*?\[!edge\][\s\S]*)/)?.[1];
|
||||
expect(insightSection).toBeDefined();
|
||||
expect(insightSection).toMatch(/> \[!edge\]/);
|
||||
expect(insightSection).toMatch(/>+ \[!edge\]/);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,28 +36,13 @@ export function renderProfileToMarkdown(
|
|||
answers: RenderAnswers,
|
||||
options?: RenderOptions
|
||||
): string {
|
||||
// WP-26: Verwende übergebene Section-Sequenz oder erstelle neue
|
||||
// Die Section-Sequenz wird während des interaktiven Wizard-Durchlaufs getrackt
|
||||
const sectionSequence: SectionInfo[] = answers.sectionSequence ? [...answers.sectionSequence] : [];
|
||||
// WP-26: Section-Sequenz und Block-IDs ausschließlich während des Render-Durchlaufs aufbauen.
|
||||
// Kein Vorfüllen aus answers.sectionSequence, damit nur Sektionen mit tatsächlichem Inhalt
|
||||
// (die gerendert werden) in der Sequenz landen – keine Kanten zu nicht existierenden Sektionen.
|
||||
const sectionSequence: SectionInfo[] = [];
|
||||
const generatedBlockIds = new Map<string, SectionInfo>();
|
||||
|
||||
console.log(`[WP-26] renderProfileToMarkdown:`, {
|
||||
sectionSequenceLength: sectionSequence.length,
|
||||
sectionSequence: sectionSequence.map(s => ({
|
||||
stepKey: s.stepKey,
|
||||
blockId: s.blockId,
|
||||
sectionType: s.sectionType,
|
||||
})),
|
||||
});
|
||||
|
||||
// WP-26: Wenn Section-Sequenz übergeben wurde, fülle generatedBlockIds aus der Sequenz
|
||||
if (answers.sectionSequence && answers.sectionSequence.length > 0) {
|
||||
for (const sectionInfo of answers.sectionSequence) {
|
||||
if (sectionInfo.blockId) {
|
||||
generatedBlockIds.set(sectionInfo.blockId, sectionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`[WP-26] renderProfileToMarkdown: sectionSequence wird beim Rendern aufgebaut (kein Vorfüllen).`);
|
||||
|
||||
const context: RenderContext = {
|
||||
profile,
|
||||
|
|
@ -998,9 +983,18 @@ function renderForwardEdges(
|
|||
}
|
||||
}
|
||||
|
||||
// WP-26: Forward-Edge generieren (prevSection -> currentSection)
|
||||
// Diese Edge wird in der aktuellen Section eingefügt
|
||||
edgeCallouts.push(`> [!edge] ${forwardEdgeType}`);
|
||||
// WP-26: In der aktuellen Section steht ein Link ZU prev → reale Kante ist current -> prev.
|
||||
// sectionEdgeTypes liefert den Type für prev -> current; hier brauchen wir die Inverse (current -> prev).
|
||||
const { vocabulary } = context.options;
|
||||
let edgeTypeForCallout = forwardEdgeType;
|
||||
if (vocabulary) {
|
||||
const canonical = vocabulary.getCanonical(forwardEdgeType);
|
||||
const inverse = canonical ? vocabulary.getInverse(canonical) : null;
|
||||
if (inverse) {
|
||||
edgeTypeForCallout = inverse;
|
||||
}
|
||||
}
|
||||
edgeCallouts.push(`> [!edge] ${edgeTypeForCallout}`);
|
||||
edgeCallouts.push(`> [[#^${resolvedPrevBlockId}]]`);
|
||||
}
|
||||
|
||||
|
|
@ -1068,19 +1062,10 @@ function renderSingleBackwardEdge(
|
|||
}
|
||||
}
|
||||
|
||||
// WP-26: Backward-Edge generieren (currentSection -> prevSection)
|
||||
// Das ist die inverse Edge zu der Forward-Edge (prevSection -> currentSection)
|
||||
let backwardEdgeType: string = forwardEdgeType;
|
||||
if (vocabulary) {
|
||||
const inverse = vocabulary.getInverse(forwardEdgeType);
|
||||
if (inverse) {
|
||||
backwardEdgeType = inverse;
|
||||
}
|
||||
}
|
||||
|
||||
// WP-26: Backward-Edge (currentSection -> prevSection) wird in prevSection eingefügt
|
||||
// Diese Edge zeigt von currentSection zurück zu prevSection
|
||||
return `> [!edge] ${backwardEdgeType}\n> [[#^${currentSection.blockId}]]`;
|
||||
// WP-26: Backward-Edge wird in prevSection eingefügt: Link von prev zu current → reale Kante ist prev -> current.
|
||||
// sectionEdgeTypes liefert genau den Type für prev -> current; also forwardEdgeType unverändert verwenden.
|
||||
// (Keine Inverse: Die Kante im Text ist prev -> current, nicht current -> prev.)
|
||||
return `> [!edge] ${forwardEdgeType}\n> [[#^${currentSection.blockId}]]`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@ export interface CaptureFrontmatterStep {
|
|||
field: string;
|
||||
required?: boolean;
|
||||
prompt?: string; // Optional prompt text
|
||||
/** Input UI: "text_line" (default) or "select" with options */
|
||||
input?: {
|
||||
kind: "text_line" | "number" | "select";
|
||||
options?: Array<{ label: string; value: string | number }>;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
};
|
||||
output?: {
|
||||
template?: string; // Template with tokens: {text}, {field}, {value}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -964,51 +964,69 @@ export class InterviewWizardModal extends Modal {
|
|||
});
|
||||
inputContainer.style.width = "100%";
|
||||
|
||||
const fieldSetting = new Setting(inputContainer);
|
||||
fieldSetting.settingEl.style.width = "100%";
|
||||
fieldSetting.controlEl.style.width = "100%";
|
||||
const fieldSetting = new Setting(inputContainer);
|
||||
fieldSetting.settingEl.style.width = "100%";
|
||||
fieldSetting.controlEl.style.width = "100%";
|
||||
|
||||
// Hide the default label from Setting component
|
||||
const settingNameEl2 = fieldSetting.settingEl.querySelector(".setting-item-name") as HTMLElement | null;
|
||||
if (settingNameEl2) {
|
||||
settingNameEl2.style.display = "none";
|
||||
}
|
||||
|
||||
fieldSetting.addText((text) => {
|
||||
text.setValue(defaultValue);
|
||||
// Store initial value
|
||||
this.currentInputValues.set(step.key, defaultValue);
|
||||
|
||||
text.onChange((value) => {
|
||||
console.log("Frontmatter field changed", {
|
||||
stepKey: step.key,
|
||||
field: step.field,
|
||||
value: value,
|
||||
});
|
||||
|
||||
// Update stored value
|
||||
this.currentInputValues.set(step.key, value);
|
||||
|
||||
this.state.collectedData.set(step.key, value);
|
||||
// Update or add patch
|
||||
const existingPatchIndex = this.state.patches.findIndex(
|
||||
p => p.type === "frontmatter" && p.field === step.field
|
||||
);
|
||||
const patch = {
|
||||
type: "frontmatter" as const,
|
||||
field: step.field!,
|
||||
value: value,
|
||||
};
|
||||
if (existingPatchIndex >= 0) {
|
||||
this.state.patches[existingPatchIndex] = patch;
|
||||
} else {
|
||||
this.state.patches.push(patch);
|
||||
// Hide the default label from Setting component
|
||||
const settingNameEl2 = fieldSetting.settingEl.querySelector(".setting-item-name") as HTMLElement | null;
|
||||
if (settingNameEl2) {
|
||||
settingNameEl2.style.display = "none";
|
||||
}
|
||||
|
||||
const applyFrontmatterPatch = (value: string | number) => {
|
||||
this.currentInputValues.set(step.key, String(value));
|
||||
this.state.collectedData.set(step.key, value);
|
||||
const existingPatchIndex = this.state.patches.findIndex(
|
||||
(p) => p.type === "frontmatter" && p.field === step.field
|
||||
);
|
||||
const patch = {
|
||||
type: "frontmatter" as const,
|
||||
field: step.field!,
|
||||
value,
|
||||
};
|
||||
if (existingPatchIndex >= 0) {
|
||||
this.state.patches[existingPatchIndex] = patch;
|
||||
} else {
|
||||
this.state.patches.push(patch);
|
||||
}
|
||||
};
|
||||
|
||||
// Select/dropdown when input.kind === "select" and options are defined
|
||||
if (step.input?.kind === "select" && Array.isArray(step.input.options) && step.input.options.length > 0) {
|
||||
const options = step.input.options;
|
||||
const optionValues = options.map((o) => String(o.value));
|
||||
const initialVal = defaultValue !== "" && optionValues.includes(String(defaultValue))
|
||||
? String(defaultValue)
|
||||
: String(options[0]?.value ?? "");
|
||||
fieldSetting.addDropdown((dropdown) => {
|
||||
for (const opt of options) {
|
||||
dropdown.addOption(String(opt.value), opt.label);
|
||||
}
|
||||
dropdown.setValue(initialVal);
|
||||
applyFrontmatterPatch(
|
||||
options.find((o) => String(o.value) === initialVal)?.value ?? options[0]?.value ?? initialVal
|
||||
);
|
||||
dropdown.onChange((value) => {
|
||||
const opt = options.find((o) => String(o.value) === value);
|
||||
const valueToStore = opt?.value ?? value;
|
||||
applyFrontmatterPatch(typeof valueToStore === "number" ? valueToStore : valueToStore);
|
||||
});
|
||||
dropdown.selectEl.style.width = "100%";
|
||||
});
|
||||
text.inputEl.style.width = "100%";
|
||||
text.inputEl.style.boxSizing = "border-box";
|
||||
text.inputEl.focus();
|
||||
});
|
||||
} else {
|
||||
fieldSetting.addText((text) => {
|
||||
text.setValue(defaultValue);
|
||||
this.currentInputValues.set(step.key, defaultValue);
|
||||
text.onChange((value) => {
|
||||
this.currentInputValues.set(step.key, value);
|
||||
applyFrontmatterPatch(value);
|
||||
});
|
||||
text.inputEl.style.width = "100%";
|
||||
text.inputEl.style.boxSizing = "border-box";
|
||||
text.inputEl.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderLoopStep(step: InterviewStep, containerEl: HTMLElement): void {
|
||||
|
|
@ -2549,9 +2567,22 @@ export class InterviewWizardModal extends Modal {
|
|||
this.saveCurrentStepData(currentStep);
|
||||
}
|
||||
|
||||
// WP-26: Zeige Übersichts-Modal für Sektions-Edges (nur wenn Sections vorhanden)
|
||||
// WP-26: Vocabulary ggf. laden (wird sonst erst in applyPatches geladen – dann wäre Dialog schon übersprungen)
|
||||
if (!this.vocabulary && this.settings?.edgeVocabularyPath) {
|
||||
try {
|
||||
const vocabText = await VocabularyLoader.loadText(
|
||||
this.app,
|
||||
this.settings.edgeVocabularyPath
|
||||
);
|
||||
this.vocabulary = parseEdgeVocabulary(vocabText);
|
||||
} catch (e) {
|
||||
console.warn("[WP-26] Vocabulary konnte nicht geladen werden (Kanten-Dialog):", e);
|
||||
}
|
||||
}
|
||||
// WP-26: Übersichts-Modal immer anzeigen, wenn Vocabulary geladen ist (einmal alle Kanten prüfen/ändern).
|
||||
// Verhindert die langsame Schritt-für-Schritt-Bearbeitung; Standardkanten können unverändert übernommen werden.
|
||||
let sectionEdgeTypes: Map<string, Map<string, string>> | undefined = undefined;
|
||||
if (this.state.sectionSequence.length > 1 && this.vocabulary) {
|
||||
if (this.vocabulary) {
|
||||
try {
|
||||
const graphSchema = this.plugin?.ensureGraphSchemaLoaded
|
||||
? await this.plugin.ensureGraphSchemaLoaded()
|
||||
|
|
@ -2567,9 +2598,10 @@ export class InterviewWizardModal extends Modal {
|
|||
|
||||
const result = await overviewModal.show();
|
||||
|
||||
if (!result.cancelled && (result.sectionEdges.size > 0 || result.noteEdges.size > 0)) {
|
||||
// Bei OK immer übernehmen (auch wenn nichts geändert wurde), damit Renderer korrekte Types/Inversen nutzt
|
||||
if (!result.cancelled) {
|
||||
sectionEdgeTypes = result.sectionEdges;
|
||||
console.log("[WP-26] Edge-Types vom Nutzer geändert:", {
|
||||
console.log("[WP-26] Edge-Types aus Übersicht übernommen:", {
|
||||
sectionEdgesCount: result.sectionEdges.size,
|
||||
noteEdgesCount: result.noteEdges.size,
|
||||
sectionEdges: Array.from(result.sectionEdges.entries()).map(([from, toMap]) => ({
|
||||
|
|
@ -2582,11 +2614,9 @@ export class InterviewWizardModal extends Modal {
|
|||
})),
|
||||
});
|
||||
|
||||
// WP-26: Speichere Note-Edges für späteres Post-Run-Edging
|
||||
// Diese werden in pendingEdgeAssignments gespeichert
|
||||
// WP-26: Note-Edges für Post-Run-Edging übernehmen
|
||||
for (const [fromBlockId, toMap] of result.noteEdges.entries()) {
|
||||
for (const [toNote, edgeType] of toMap.entries()) {
|
||||
// Finde Section-Key für fromBlockId
|
||||
const sectionInfo = fromBlockId === "ROOT"
|
||||
? null
|
||||
: this.state.sectionSequence.find(s => s.blockId === fromBlockId);
|
||||
|
|
|
|||
|
|
@ -408,17 +408,31 @@ export class SectionEdgesOverviewModal extends Modal {
|
|||
return section.noteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Section im Dialog Inhalt hat (wird später gerendert).
|
||||
* Nur Kanten zwischen Sections mit Inhalt werden angezeigt, damit keine
|
||||
* Kanten zum Abgleich erscheinen, die später nicht generiert werden.
|
||||
*/
|
||||
private hasSectionContent(section: SectionInfo): boolean {
|
||||
const value = this.collectedData.get(section.stepKey);
|
||||
if (value === undefined || value === null) return false;
|
||||
const str = typeof value === "string" ? value : String(value);
|
||||
return str.trim() !== "";
|
||||
}
|
||||
|
||||
private buildSectionEdgeList(): void {
|
||||
this.sectionEdges = [];
|
||||
|
||||
for (let i = 1; i < this.sectionSequence.length; i++) {
|
||||
const currentSection = this.sectionSequence[i];
|
||||
if (!currentSection || !currentSection.blockId) continue;
|
||||
if (!this.hasSectionContent(currentSection)) continue;
|
||||
|
||||
// Alle vorherigen Sections
|
||||
for (let j = 0; j < i; j++) {
|
||||
const prevSection = this.sectionSequence[j];
|
||||
if (!prevSection || !prevSection.blockId) continue;
|
||||
if (!this.hasSectionContent(prevSection)) continue;
|
||||
|
||||
// WP-26: Verwende effektive Section-Types (mit Heading-Level-basierter Fallback-Logik)
|
||||
const prevType = this.getEffectiveSectionType(prevSection, j);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user