---
title: 'Concepts verlinken — eine erlaubte Linkform (Story 2.3)'
type: 'feature'
created: '2026-08-17'
status: 'done'
review_loop_iteration: 1
baseline_commit: 7e1f449bf78741bb6739d8f510f3ab5543617fef
context:
- _bmad-output/implementation-artifacts/epic-2-context.md
---
## Intent
**Problem:** Die Link-Schicht von `wiki/` existiert bisher nur in `index.md`; AD-7b/A0-9 („genau eine Form: bundle-relativ, mit oder ohne Endung — nie beide") ist noch formal offen — `schema/compiler.md` (§5.3 Pkt.3, §5.5 Pkt.1) und `schema/validator.md` (Punkt 11, §8) tragen alle die Klausel „Festlegung ist Story 2.3". Zwei Producer könnten aus demselben Baum unterschiedliche IDs berechnen; es existieren noch keine Concept-zwischen-Concept-Links (AD-8).
**Approach:** Die Linkform wird auf **bundle-relativ mit `.md`-Endung** gepinnt (alle 3 bestehenden Concept-Links in `wiki/index.md` sind bereits in dieser Form → Null-Migration; die 3 `../schema/`-Links in `index.md` liegen außerhalb des Pin-Wirkungsbereichs (andere Schicht) und bleiben unberührt; Ziele sind explizite Dateien, auch mit Areas eindeutig; Standard-Markdown-Tools lösen ohne Konventionswissen auf, AD-8). Gepinnt wird die Form in `schema/compiler.md` (neue §5.6 + re-executierbare Selbsttest-Formeln + Auflösung der „bis Story 2.3"-Klauseln); demonstriert in zwei inhaltsbasierten Cross-Links in **einem** Concept-Body (`wiki/wissensarchitektur-trennung-states.md` — die Ziel-Concepts werden nicht mutiert, da keine inhaltsbegründete Gegenverbindung besteht: keine erzwungene Gegenseitigkeit, AD-8); dokumentiert in `wiki/log.md`. Der Validator bleibt strukturell unverändert (Präzedenz Story 2.2, D-3; Punkt 11 prüft weiter nur das Vorhandensein von Links).
## Boundaries & Constraints
**Always:**
- Gepinnte Form: `[]()` — genau eine Form; Ziel = Concept-OKF-Identität (AD-7a) + `.md` (AD-7b, A0-9). Links gehören in Concept-Bodies und `wiki/index.md`; Body-Links ändern keine Aussagen, `sources` oder Frontmatter (Navigations-/Beziehungsschicht ≠ Provenienz, AD-8).
- `schema/compiler.md` ist einziger Instruktions-Ort (D-3): neue §5.6 „Concept-Links (Story 2.3)" — Pin, Geltungsbereich (Concept-Links + `index.md`-Links; `raw/`-Provenienz-Verweise nach §5.5 bleiben in Plain-/Komma-Form, andere Schicht), Selbsttest-Formeln (re-executierbar, AD-17h), Worked Example; die „bis Story 2.3"-Klauseln in §5.3 Pkt.3 und §5.5 Pkt.1 werden auf §5.6 referenziert; §7-Selbstbegrenzung-Bullet umformuliert; §8-Revisionslog (1.8) + Normreferenzen (AD-7b/AD-8, A0-9, FR-10).
- **§5.6-Inhalts-Pflichten (Verifikations-Vertrag):** §5.6 MUSS enthalten — (a) exakt eine Pin-Zeile in der oben gepinnten Form, gerendert ohne verschachtelte Backticks (innerer Code-Fence = Anführungszeichen, kein zweites Backtick-Paar); (b) Geltungsbereich = Concept-Bodies + `wiki/index.md`; explizit ausgenommen: `raw/`-Provenienz-Verweise (§5.5, andere Schicht), `../schema/`-Links, `http`-Links, **Gleichseit-Anker `(#…)`** (Form-Check und Dangling-Check exkludieren Anker); (c) **vier** re-executierbare Selbsttest-Formeln — Bestands-Check, Form-Check (erwartet `0`), Dangling-Check (erwartet keine Ausgabe), **Kontakt-mit-`raw/`-Unverändert-Check** (deterministische Baseline-Extraktion aus `baseline_commit` via `git show`, AD-17h — keine „identisch zur X-Baseline"-Behauptung ohne extrahierbare Baseline); alle vier Formeln deterministisch re-executierbar (AD-17h) und bei `wiki/`-Area-Subtree (Story 2.4/2.5) lauffest (rekursive Erfassung, keine künftigen Area-Concepts verpasst); (d) NFR-4-Regel: jede Form-Verletzung → Run-FAIL mit textuell benannter Ursache; (e) Worked Example (gepinnter Link + Form-Check-Auflösung). Die Formeln sind so zu halten, dass die §6.6-Referenztabellen (`Positiv-/Negativ-Beispiele`) eine §5.6-Zeile tragen können (✓-Vorgabe / ✗-Formfehler → deterministische Ursache).
- Body-Cross-Links inhaltsbegründet (nicht mechanisch): `wiki/wissensarchitektur-trennung-states.md` → `llm-wiki-prinzip.md` (das Wiki-Concept ist der Gegenstand des Architektur-States „curated bundle") und → `knowledge-kompilation-inkrementell.md` (die Kompilation realisiert die Trennung); jeweils ein kurzer Kontextsatz an semantisch passender Stelle. Die Kontextsätze sind **§5.5-konform zu formulieren**: jede darin belegte Aussage trägt entweder einen Inline-`raw/`-Verweis oder einen Kontext-Marker („… nicht eigenständig belegt") — keine unbelegte neue Behauptung im Body.
- `wiki/log.md`: datumsgruppierter Eintrag (Vertrag §5-Format, neueste zuerst). Der Eintrag MUSS die Tatsachen des Runs abbilden: (a) Formel-Zählung identisch mit §5.6 (vier Selbsttest-Formeln); (b) `sprint-status.yaml`-Statuswechsel `2-3-…` `backlog` → `in-progress` **dokumentieren** (dieser findet statt — kein „bereits in-progress"); (c) `raw/`-Unverändert-Beleg als extrahierbare Baseline-Aussage (Zahl + `baseline_commit`), nicht als bloße „identisch"-Behauptung. `sprint-status.yaml`: Key `2-3-…` → `in-progress`.
- **Evidenz-Auflösung (re-executierbar, AD-17h):** Jede im Code Map / Verification / log-Eintrag stehende Zählung-Behauptung über den Ausgangszustand ist an `baseline_commit` (Frontmatter) zu extrahieren — z. B. `git show 7e1f449:wiki/index.md` für den Link-Bestand, `git show 7e1f449:… | grep` für `raw/`-Treffer — keine freistehende Zahl ohne extrahierbare Quelle.
**Ask First:** Andere Pin-Wahl (mit vs. ohne `.md`) · irgendeine Änderung an `schema/validator.md` (Punkt-11-Einschränkung, neue §7-Klasse) · neue Concepts/Areas (Story 2.4/2.5).
**Never:** Änderung an `schema/wiki-compiler.md`/`schema/validator.md`/`raw/` (AD-3; der Validator akzeptiert bis auf Weiteres beide Formen — Einschränkung wäre eigene Autorisierung) · Links in Frontmatter/`sources` (AD-4b) · neue §7-Invaliditätsklasse · Standalone-Tool (D-3) · Vorwegnahme Area-Zuordnung/Discovery (Story 2.4/2.5) · Renames (AD-7d) · OKF-Dialekt.
## I/O & Edge-Case Matrix
| Scenario | Input / State | Expected Output / Behavior | Error Handling |
|----------|--------------|---------------------------|----------------|
| HAPPY_PATH | Bundle nach dem Pin | Genau eine Linkform in `wiki/`; 2 neue Body-Links auflösbar; Validator-Lauf SUCCESS (5 `wiki/`-Dateien) | N/A |
| FORMVERLETZUNG | Concept-Link ohne `.md` (oder Mischform) | Selbsttest-Formel (Pkt. 3, Form-Check) trifft deterministisch; Link wird korrigiert oder entfernt, bevor veröffentlicht | Run-FAIL textuell benannt (NFR-4) |
| DANGLING_LINK | Body-Link auf nicht existente `wiki/`-Datei | Dangling-Formel (Pkt. 3) trifft; Link wird korrigiert oder entfernt | Run-FAIL textuell benannt (NFR-4) |
| GLEICHSSEIT_ANKER | Gleichseit-Anker-Link `(#…)` im Body | von Form-Check und Dangling-Check exkludiert — keine falsche FAIL/Dangling-Meldung; bleibt außerhalb des Pins | N/A |
| KONTAKT_MIT_RAW | `raw/`-Provenienz-Verweis (§5.5) im Body | bleibt in Plain-/Komma-Form — der Pin berührt ihn nicht (Selbsttest-Formel `\(raw/` ergibt unveränderte Ausgabe zur `baseline_commit`-Extraktion) | N/A |
## Code Map
- `schema/compiler.md` — **mutiert**: neue §5.6 „Concept-Links (Story 2.3)" (Pin, Geltungsbereich, Selbsttest-Formeln, Worked Example) nach §5.5, vor §6 (L123); „bis Story 2.3"-Klauseln in §5.3 Pkt.3 (L62) und §5.5 Pkt.1 (L78) → Verweis auf §5.6; §7-Bullet (L174); §8-Revisionslog (L198 → Eintrag 1.8) + Normreferenzen (AD-7b/AD-8, A0-9, FR-10).
- `wiki/wissensarchitektur-trennung-states.md` — **mutiert**: 2 Body-Cross-Links (→ `llm-wiki-prinzip.md`, → `knowledge-kompilation-inkrementell.md`) je mit kurzem Kontextsatz; ansonsten unverändert.
- `wiki/llm-wiki-prinzip.md`, `wiki/knowledge-kompilation-inkrementell.md` — **mutiert nur bei** inhaltsbegründeter Gegenrichtung; sonst unverändert.
- `wiki/index.md` — **read-only (konform)**: die 3 Concept-Links (L29–31) sind bereits in der gepinnten Form (`.md`) → kein Change; die 3 `../schema/`-Links (L33) liegen außerhalb des Pin-Wirkungsbereichs (andere Schicht) → kein Change; Punkt-11-Check.
- `wiki/log.md` — **append**: Eintrag unter `## 2026-08-17`.
- `schema/wiki-compiler.md` (L58: AD-8 Navigations-Schicht), `schema/validator.md` (Punkt 11, L70; §8, L297 — „Story 2.3"-Notiz bleibt) — **read-only**.
- `raw/architecture-spine/…` (AD-7b L246–248, AD-8 L260–268), `raw/epics/…` (A0-9 L57) — **read-only Evidenz**.
- `_bmad-output/implementation-artifacts/deferred-work.md` (L66–68: der Linkform-Eintrag, aktuell ohne `status:`-Zeile) und `sprint-status.yaml` (L48) — **mutiert** (Eintrag schließen / Status).
## Tasks & Acceptance
**Execution:**
- [x] `schema/compiler.md` — neue §5.6 „Concept-Links (Story 2.3)": Pin (`…/.md`), Geltungsbereich + `raw/`-Exklusion, 4 Selbsttest-Formeln (bestands-, form-, dangling-, `raw/`-Unverändert-Check; re-executierbar), Worked Example; „bis Story 2.3"-Klauseln (§5.3 Pkt.3, §5.5 Pkt.1) auf §5.6 umformuliert; §7-Bullet angepasst; §8-Revisionslog 1.8 + Normreferenzen — AD-7b/AD-8/A0-9, D-3, AD-17h.
- [x] `wiki/wissensarchitektur-trennung-states.md` — 2 inhaltsbegründete Cross-Links in der gepinnten Form, je ein kurzer Kontextsatz (Link = Navigation, keine Provenienz-Änderung).
- [x] `wiki/log.md` — Eintrag: Pin-Entscheidung (mit `.md`; Rationale: Null-Migration, explizite Datei-Ziele, Standard-Tools), veränderte Dateien, Validator-Lauf-Ergebnis.
- [x] `sprint-status.yaml` — `2-3-…` → `in-progress`.
- [x] `deferred-work.md` — Eintrag L66–68 um `status:`-Zeile schließen (Format wie die bestehenden `status: umgesetzt (…)`-Einträge).
- [x] Verifikation — alle Formeln aus der Verification-Sektion re-executieren + Validator-Lauf (5 `wiki/`-Dateien SUCCESS).
**Acceptance Criteria:**
- Given zwei zusammengehörige Concepts, when eine Beziehung ausgedrückt wird, then nutzt sie einen normalen Markdown-Link in genau einer erlaubten Form — bundle-relativ mit `.md`-Endung (AD-7b, A0-9).
- Given der Link-Form-Standard, when ein Consumer die Links traversiert, then sind die Ziel-Concepts ohne Wiki-Software auffindbar (alle internen Links auflösen; FR-10, AD-8).
- Given ein Link, when er gespeichert wird, then verändert er weder Concept-Inhalt noch `sources`/Frontmatter (Selbsttest-Formel `\(raw/` liefert identische Ausgabe wie vor der Änderung).
- Given die Instruktion, when geprüft, then §5.6 pinnt die Form, die Selbsttest-Formeln sind deterministisch re-executierbar (AD-17h), Validator/Vertrag/`raw/` sind unverändert und das Bundle bleibt Validator-SUCCESS (keine neue §7-Klasse).
## Spec Change Log
- **2026-08-17 (Erstellung):** Initiale Approve-Baseline.
- **2026-08-17 (Loop 1, Step-04-Review, bad_spec):** Auslösender Befund: Die Verification-Zeile `KONTAKT_MIT_RAW`-Baseline behauptete „identisch zur Story-2.2-Baseline (27 Treffer)"; die reale, an `baseline_commit` extrahierbare Trefferzahl beträgt **34 Vorkommen (28 Treffer-Zeilen)** — die Zahl „27" war nicht re-executierbar (verstoß gegen AD-17h) und damit ein direkter Spezifikationsfehler (nicht nur ein Implementierungs-Fehler). **Geändert:** (1) Verification `KONTAKT_MIT_RAW`-Zeile auf deterministische Baseline-Extraktion aus `baseline_commit` umgestellt (`git show 7e1f449:… | grep -oE '\(raw/' | wc -l` → 34 Vorkommen; aktuell ≡ Baseline), keine freistehende Zahl; (2) Approach-Überbehauptung „alle 6 bestehenden Links sind bereits in dieser Form" korrigiert zu „alle 3 bestehenden Concept-Links in `index.md`" (die 3 `../schema/`-Links liegen außerhalb des Pin-Wirkungsbereichs); (3) Approach „demonstriert in zwei … Concept-Bodies" korrigiert zu „in **einem** Concept-Body" (nur `wissensarchitektur-trennung-states.md` mutiert — keine erzwungene Gegenseitigkeit, AD-8); (4) I/O-Matrix `FORMVERLETZUNG` um „oder entfernt" ergänzt und Falsch-Verweis „(Pkt. 2 unten)" → „(Pkt. 3, Form-Check)"; neue Matrix-Zeile `GLEICHSSEIT_ANKER`; (5) Always-Bullets um §5.6-Inhalts-Pflichten (vier Formeln inkl. `raw/`-Unverändert-Check mit extrahierbarer Baseline; Anker-Exklusion; Area-robustheit; §5.5-konforme Kontextsätze; log-Eintrag dokumentiert den `sprint-status`-Wechsel und die extrahierbare `raw/`-Baseline) und um Evidenz-Auflösungs-Pflicht (Zählungen an `baseline_commit` extrahieren); (6) Code Map `index.md` um `../schema/`-Ausnahme präzisiert. **Vermeideter Known-Bad-State:** eine Spezifikation, die eine nicht re-executierbare Zählung (27) als Baseline nennt und damit die AD-17h-Determinismus-Eigenschaft des eigenen Artefakts verletzt. **KEEP-Instructions (positive Erhaltung bei Re-Derivation):** (a) Pin-Form `bundle-relativ mit .md` und deren Rationale (Null-Migration der 3 Concept-Links, explizite Datei-Ziele, Standard-Tools) — unverändert beibehalten; (b) die drei Kern-Selbsttest-Formeln (Bestands-/Form-/Dangling-Check) in der bewährten, negativ-geprüften `sh -c`-Form — beibehalten, um `raw/`-Unverändert-Check (vierte Formel) ergänzt; (c) „Validator/Vertrag/`raw/` unverändert, keine neue §7-Klasse, kein Standalone (D-3)" — strikt beibehalten; (d) §5.6 als einziger Instruktions-Ort (D-3) — beibehalten; (e) die 2 inhaltsbegründeten Cross-Links (ohne erzwungene Gegenseitigkeit) — beibehalten, Kontextsätze jedoch §5.5-konform (Inline-`raw/`-Verweis oder Kontext-Marker) zu formulieren.
## Verification
**Commands (re-executierbar, ab Workspace-Root; alle vier Formeln AD-17h-konform und negativ-geprüft):**
Alle Formeln scannen `wiki/` rekursiv (künftige Area-Concepts unter `wiki//`, Story 2.4) und exkludieren `log.md` (Dokumentation, keine Link-/Provenienz-Schicht — sie enthält die Formel-Texte selbst als Zitate und würde die Zählungen verunreinigen).
1. **Bestands-Check** (Link-Überblick):
`sh -c "grep -roE ']\([^)]*\)' --include='*.md' --exclude=log.md wiki/"`
— erwartet: 6 `index.md`-Links (3 Concept mit `.md`, 3 `../schema/`) + 2 neue Body-Links (beide `.md`); alle internen Concept-Ziele `*.md`. (`[^)]*` statt `[^)]+` — leere Ziele `]()` werden sichtbar statt unsichtbar.)
2. **Form-Check** (erwartet Ausgabe `0`, Exit `0`):
`sh -c "grep -rohE ']\([^)]*\)' --include='*.md' --exclude=log.md wiki/ | sed -E 's/^\]\(//; s/\)$//' | sort -u | grep -vE '^(raw/|\.\./|#)' | grep -vE ':' | grep -cvE '^[^#]+\.md$' || true"`
— jeder interne Link in der gepinnten Form (`.md`-Endung); exkludiert: `raw/`-Provenienz (§5.5, andere Schicht), `../schema/`, externe Ziele (alle mit `:` — `http://`, `https://`, `mailto:`, protocol-less Hostnamen; interne OKF-Ziele sind Kebab-Case und enthalten nie `:`), Gleichseit-Anker `(#…)`. `|| true` bindet den Exit-Code (grep `-c` liefert Exit `1` bei Ausgabe `0` — die *gewünschte* SUCCESS-Konfiguration). Negativ-geprüft: fehlende `.md`-Endung liefert `1`; `mailto:`/`https://` exkludiert; `]()` wird gezählt. (Cross-Page-Anker `file.md#sec` werden weiterhin gezählt — normative Frage, s. `deferred-work.md`.)
3. **Dangling-Check** (erwartet keine Ausgabe):
```sh
sh -c 'grep -rohE "]\([^)]*\)" --include="*.md" --exclude=log.md wiki/ | sed -E "s/^\]\(//; s/\)$//" | sort -u | while read -r t; do case "$t" in ""|*:*|raw/*|../*) if [ "$t" = "" ]; then echo "DANGLING: (leeres Ziel)"; fi; continue;; esac; case "$t" in "#"*) continue;; esac; p=${t%%#*}; [ -f "wiki/$p" ] || echo "DANGLING: $t"; done'
```
— jedes interne Ziel existiert relativ zum Bundle-Root (bundlerelativ — das Auflösungsmodell des Bundles, AD-7b); Fragment wird vor dem Existenztest gestripped (`file.md#sec` → `file.md`, kein falscher `DANGLING` für existierende Ziele — die Pin-Form-Frage bleibt beim Form-Check); exkludiert wie beim Form-Check; leere Ziele `]()` werden als `DANGLING: (leeres Ziel)` gemeldet, nicht still exkludiert. Negativ-geprüft: nicht existierendes Ziel liefert `DANGLING: `; `]()` liefert `DANGLING: (leeres Ziel)`; `https://…`/`#anker` exkludiert; `concepts.md#s1` (existierende Datei) liefert **keine** Ausgabe. (Bekannt-konservativ: Ziele mit `)` werden am ersten `)` abgeschnitten → falsch benannte Ursache, aber keine Stille — dokumentiert in `deferred-work.md`.)
4. **Kontakt-mit-`raw/`-Unverändert-Check** (aktuell ≡ Baseline, beide re-executierbar, AD-17h):
- aktuell: `sh -c "grep -roE '\(raw/' --include='*.md' --exclude=log.md wiki/ | wc -l"`
- Baseline an `baseline_commit` extrahiert (Dateimenge dynamisch aus dem Commit abgeleitet — keine handgelistete Pfadliste, keine Newline-Verbindungs-Abhängigkeit; **einschließende** Einzelanführungszeichen, damit `$f` erst im inneren Shell expandiert):
`sh -c 'git ls-tree -r --name-only 7e1f449bf78741bb6739d8f510f3ab5543617fef -- wiki/ | grep -v "wiki/log.md$" | while read -r f; do git show "7e1f449bf78741bb6739d8f510f3ab5543617fef:$f"; done | grep -oE "\(raw/" | wc -l'`
— beide Vorkommen-Zählungen müssen identisch sein (erwartet **30**; der Check setzt die unveränderte `wiki/`-Dateimenge voraus — gilt für diesen Run; bei Datei-Zuwachs in späteren Runs ist die Baseline-Extraktion neu durchzuführen). Der Pin berührt `raw/`-Provenienz-Verweise nicht. (Ersetzt die frühere, nicht re-executierbare Formulierung „identisch zur Story-2.2-Baseline (27 Treffer)" und die handgelistete 5-Datei-`git show`-Kette.)
5. **Validator-Lauf** (deterministisch, D-3 manuell-mechanisch): alle 5 `wiki/`-Dateien SUCCESS (Punkte 1/6/11/14, EC-1).
**Manual checks:**
- Cross-Links inhaltsbegründet (Kontextsatz, keine erzwungene Gegenseitigkeit); Kontextsätze §5.5-konform (Inline-`raw/`-Verweis oder Kontext-Marker); keine Frontmatter-/`sources`-Änderung; `log.md`-Eintrag datiert, im Vertrag-§5-Format, dokumentiert den `sprint-status`-Wechsel und die extrahierbare `raw/`-Baseline; `sprint-status.yaml` und `deferred-work.md` konsistent.
## Suggested Review Order
**Pin & Selbsttest-Formeln (Konvention) — Einstieg**
- Der eigentliche Change: §5.6 mit Pin, Geltungsbereich, vier Formeln, NFR-4, Worked Example.
[`compiler.md:123`](../../schema/compiler.md#L123)
- §6.6 trägt die §5.6-Zeile: ✓ gepinnte Form / ✗ fehlende `.md` → Run-FAIL.
[`compiler.md:213`](../../schema/compiler.md#L213)
- Revisionslog 1.8/1.9: vollständige Änderungsgeschichte inkl. Loop-1-Schärfungen der Formeln.
[`compiler.md:251`](../../schema/compiler.md#L251)
**Demonstration**
- Der Demonstrations-Run: zwei Cross-Links mit §5.5-konformem Kontext (L20, L22).
[`wissensarchitektur-trennung-states.md:20`](../../wiki/wissensarchitektur-trennung-states.md#L20)
**Dokumentation & Tracking**
- Story-2.3-Eintrag: Rationale, `sprint-status`-Wechsel, `raw/`-Baseline (30 ≡ 30), Validator-Lauf.
[`log.md:4`](../../wiki/log.md#L4)
- Linkform-Eintrag (spec-1-4) geschlossen; vier neue Defer-Einträge am Dateiende.
[`deferred-work.md:69`](./deferred-work.md#L69)
- Statuswechsel `backlog` → `in-progress` (→ `review` mit diesem Schritt).
[`sprint-status.yaml:48`](./sprint-status.yaml#L48)