# WP3-001-005 – Implementation Plan  
## Runtime Integration

- **Status:** Proposed for PL approval
- **Version:** 0.1
- **Scope:** WP3 – Discovery Framework Foundation
- **Basis:** aktueller Projektstand `reconcile_ADR002.WP2_20260801_4.zip`
- **Related:** WP3-001-005 – Runtime Integration v0.2

---

# 1. Ziel

WP3-001-005 integriert das neue Discovery Framework in den produktiven Reconcilix-Laufzeitpfad.

Nach Abschluss dieses Teilpakets wird der `ReconciliationApplicationService` nicht mehr direkt mit dem bisherigen `LegacyCandidateDiscoveryAdapter` verbunden. Stattdessen erhält er den bereits implementierten `CandidateDiscoveryRouter` als `CandidateDiscoveryGateway`.

Die bestehende Candidate Discovery bleibt dabei fachlich unverändert und wird vorübergehend über einen `LegacyDiscoveryCompatibilityAdapter` hinter dem neuen Framework weiterverwendet.

---

# 2. Aktueller Laufzeitpfad

Der aktuelle Projektstand verdrahtet die Candidate Discovery in `RuntimeFactory` direkt:

```text
ReconciliationApplicationService
        │
        ▼
CandidateDiscoveryGateway
        │
        ▼
LegacyCandidateDiscoveryAdapter
        │
        ▼
ReconciliationOrchestrator
        │
        ▼
bestehender Provider- und Local-Store-Pfad
        │
        ▼
DiscoveredCandidate[]
```

Die direkte Verdrahtung erfolgt derzeit in:

```text
src/Infrastructure/Factory/RuntimeFactory.php
```

Der `LegacyCandidateDiscoveryAdapter` implementiert aktuell unmittelbar den `CandidateDiscoveryGateway`.

---

# 3. Ziel-Laufzeitpfad

Nach WP3-001-005 gilt:

```text
ReconciliationApplicationService
        │
        ▼
CandidateDiscoveryGateway
        │
        ▼
CandidateDiscoveryRouter
        │
        ├── DiscoveryConfigurationRegistry
        │       └── DiscoveryRoute[]
        │
        ├── CandidateDiscoveryAdapterRegistry
        │       └── adapterKey
        │
        ▼
LegacyDiscoveryCompatibilityAdapter
        │
        ▼
LegacyCandidateDiscoveryAdapter
        │
        ▼
ReconciliationOrchestrator
        │
        ▼
bestehender Provider- und Local-Store-Pfad
        │
        ▼
DiscoveredCandidate[]
```

Der zusätzliche Wrapper um den bestehenden `LegacyCandidateDiscoveryAdapter` ist bewusst temporär.

Er vermeidet:

- eine Kopie der bestehenden Legacy-Mappinglogik,
- eine vorzeitige Änderung des funktionierenden Legacy Adapters,
- eine Vermischung von WP3-001-005 mit WP3-002 oder WP3-003.

---

# 4. Neue Produktionsdatei

## 4.1 LegacyDiscoveryCompatibilityAdapter

**Pfad**

```text
src/Infrastructure/Discovery/Adapter/LegacyDiscoveryCompatibilityAdapter.php
```

**Namespace**

```php
App\Infrastructure\Discovery\Adapter
```

**Typ**

```php
final readonly class LegacyDiscoveryCompatibilityAdapter implements CandidateDiscoveryAdapter
```

**Abhängigkeit**

```php
App\Infrastructure\Reconciliation\LegacyCandidateDiscoveryAdapter
```

**Konstruktor**

```php
public function __construct(
    private LegacyCandidateDiscoveryAdapter $legacyDiscovery,
)
```

**Contract**

```php
/** @return list<DiscoveredCandidate> */
public function discover(ReconciliationCommand $command): array
```

**Verhalten**

Die Methode delegiert den `ReconciliationCommand` unverändert an den bestehenden `LegacyCandidateDiscoveryAdapter` und gibt dessen `DiscoveredCandidate[]` unverändert zurück.

Der Compatibility Adapter enthält:

- keine neue Discovery-Logik,
- kein eigenes Mapping,
- keine Route-Auswahl,
- keine Provider-Auswahl,
- keine Persistenzlogik.

---

# 5. Discovery Routes für die Übergangsphase

WP3-001-005 benötigt für jeden derzeit konfigurierten Vocabulary Scope genau eine aktive Route auf den Compatibility Adapter.

Für die Übergangsphase wird ein gemeinsamer Adapter-Key verwendet:

```text
legacy-discovery
```

Die Routen werden aus der bestehenden Vocabulary-Konfiguration erzeugt.

## 5.1 Root Vocabularies

Für jedes konfigurierte `Vocabulary`:

```text
targetVocabularyUri = Vocabulary.id
scopeVocabularyUri  = Vocabulary.id
candidateSource     = legacy-reconciliation-flow
discoveryMethod     = legacy-orchestrator
adapterKey          = legacy-discovery
enabled             = true
```

## 5.2 SubVocabularies

Für jedes konfigurierte `SubVocabulary`:

```text
targetVocabularyUri = SubVocabulary.conceptSchemeId
scopeVocabularyUri  = SubVocabulary.id
candidateSource     = legacy-reconciliation-flow
discoveryMethod     = legacy-orchestrator
adapterKey          = legacy-discovery
enabled             = true
```

Beispiel MCT-Objektfacette:

```text
targetVocabularyUri = http://matcult-the.vocnet.org
scopeVocabularyUri  = http://matcult-the.vocnet.org/00000019
adapterKey           = legacy-discovery
```

Die Route enthält keine Kenntnis darüber, ob der Legacy-Pfad intern Local Store oder xTree verwendet. Diese Entscheidung verbleibt bis WP3-002 und WP3-003 im bestehenden `ReconciliationOrchestrator`.

---

# 6. Änderungen an RuntimeFactory

**Datei**

```text
src/Infrastructure/Factory/RuntimeFactory.php
```

## 6.1 Neue Imports

```php
CandidateDiscoveryAdapterRegistry
LegacyDiscoveryCompatibilityAdapter
DiscoveryConfigurationRegistry
DiscoveryRoute
CandidateDiscoveryRouter
Vocabulary
SubVocabulary
```

## 6.2 Neue Konstante

```php
private const LEGACY_ADAPTER_KEY = 'legacy-discovery';
```

## 6.3 create()-Methode

Die bisherige direkte Erstellung:

```php
new LegacyCandidateDiscoveryAdapter(...)
```

wird durch folgende Kette ersetzt:

```text
LegacyCandidateDiscoveryAdapter
        ↓
LegacyDiscoveryCompatibilityAdapter
        ↓
CandidateDiscoveryAdapterRegistry
        ↓
DiscoveryConfigurationRegistry
        ↓
CandidateDiscoveryRouter
        ↓
ReconciliationApplicationService
```

Der `ReconciliationApplicationService` erhält:

```php
candidateDiscovery: $router
```

## 6.4 Private Hilfsmethoden

Zur Begrenzung der `create()`-Methode werden zwei private Methoden vorgesehen:

```php
private function createDiscoveryRoutes(): array
```

```php
private function createLegacyDiscoveryAdapter(
    RuntimeContext $runtimeContext,
): LegacyDiscoveryCompatibilityAdapter
```

`createDiscoveryRoutes()` erzeugt ausschließlich die Übergangsrouten aus den vorhandenen `Vocabulary`- und `SubVocabulary`-Objekten.

Die Methode:

- erzeugt keine Prioritäten,
- erzeugt keine Fallback-Routen,
- prüft keine Tenant-Berechtigungen,
- interpretiert keine Reconciliation Stores,
- verändert die bestehende Vocabulary-Konfiguration nicht.

---

# 7. Änderungen am Composition Root

**Datei**

```text
src/Infrastructure/Composition/ReconciliationCompositionRoot.php
```

Der Composition Root bleibt verantwortlich für:

- Aufbau des bestehenden `ProviderRegistry`,
- Aufbau des `ReconciliationOrchestrator`,
- Übergabe der Vocabulary-Konfiguration an `RuntimeFactory`,
- Erzeugung des `RuntimeFactory`.

Es ist keine zusätzliche Discovery-spezifische Factory im Composition Root erforderlich.

Die eigentliche tenant-gebundene Verdrahtung erfolgt weiterhin in `RuntimeFactory`, weil der bestehende Legacy-Discovery-Pfad einen `TenantContext` benötigt.

Damit bleibt die bestehende Grenze erhalten:

```text
Composition Root
        │
        └── erstellt langlebige Infrastruktur

RuntimeFactory.create(TenantContext)
        │
        └── erstellt tenant-gebundene Processing Unit
```

Für WP3-001-005 sind am Composition Root voraussichtlich nur Import- oder Argumentanpassungen notwendig, falls sich die Signatur des `RuntimeFactory` verändert.

Der Implementation Plan sieht derzeit keine zwingende Signaturänderung vor.

---

# 8. Bestehender LegacyCandidateDiscoveryAdapter

**Datei**

```text
src/Infrastructure/Reconciliation/LegacyCandidateDiscoveryAdapter.php
```

Die bestehende Klasse bleibt unverändert.

Sie:

- implementiert weiterhin `CandidateDiscoveryGateway`,
- enthält weiterhin die bestehende Mappinglogik,
- wird innerhalb des Compatibility Adapters weiterverwendet,
- ist nach WP3-001-005 nicht mehr direkt mit dem Application Service verbunden.

Die direkte Gateway-Implementierung ist während der Übergangsphase toleriert. Eine Entfernung oder Umbenennung erfolgt nicht in WP3-001-005.

Damit bleibt das Paket ein Integrations-Refactoring und wird nicht zu einer Legacy-Bereinigung ausgeweitet.

---

# 9. Neue Testdateien

## 9.1 LegacyDiscoveryCompatibilityAdapterTest

**Pfad**

```text
tests/Infrastructure/Discovery/Adapter/LegacyDiscoveryCompatibilityAdapterTest.php
```

Da `LegacyCandidateDiscoveryAdapter` eine konkrete `final`-Klasse ist, wird der Test mit einem realen Legacy Adapter und einer minimalen Legacy-Infrastruktur aufgebaut.

**Testfälle**

1. Compatibility Adapter implementiert `CandidateDiscoveryAdapter`.
2. `ReconciliationCommand` wird fachlich unverändert verarbeitet.
3. Das Ergebnis ist `DiscoveredCandidate[]`.
4. Ein leerer Legacy-Ergebnisweg wird unverändert als leere Liste zurückgegeben.
5. Der Compatibility Adapter enthält keine eigene Mapping- oder Routinglogik.

**Erwartete Ausgabe**

```text
WP3-001-005 Legacy Discovery Compatibility Adapter: OK
```

---

## 9.2 RuntimeFactoryDiscoveryIntegrationTest

**Pfad**

```text
tests/Factory/RuntimeFactoryDiscoveryIntegrationTest.php
```

**Testziel**

Nachweisen, dass `RuntimeFactory` den neuen Runtime-Pfad erzeugt.

**Testfälle**

1. `RuntimeFactory` implementiert weiterhin `ApplicationServiceFactory`.
2. Für jede Processing Unit wird ein neuer `ReconciliationApplicationService` erzeugt.
3. Der Application Service erhält intern einen `CandidateDiscoveryRouter`.
4. Der Router besitzt eine `DiscoveryConfigurationRegistry`.
5. Der Router besitzt eine `CandidateDiscoveryAdapterRegistry`.
6. Root-Vocabulary-Routen werden erzeugt.
7. SubVocabulary-Routen werden erzeugt.
8. Der Adapter-Key `legacy-discovery` ist auflösbar.
9. Zwei Tenant-Kontexte erhalten getrennte Router- und Adapterinstanzen.
10. Die vorhandene RuntimeContext-Regel bleibt unverändert.

Für die Prüfung der privaten Verdrahtung wird im Test eine begrenzte Reflection-Inspektion verwendet. Produktivcode erhält dafür keine Test-Getter.

**Erwartete Ausgabe**

```text
WP3-001-005 Runtime Factory Discovery Integration: OK
```

---

## 9.3 DiscoveryRuntimeRegressionTest

**Pfad**

```text
tests/Integration/DiscoveryRuntimeRegressionTest.php
```

**Testziel**

Vergleich des bisherigen direkten Legacy-Pfads mit dem neuen Router-Pfad.

Für denselben `ReconciliationCommand` werden ausgeführt:

```text
A: LegacyCandidateDiscoveryAdapter direkt
B: CandidateDiscoveryRouter
   → LegacyDiscoveryCompatibilityAdapter
   → LegacyCandidateDiscoveryAdapter
```

Verglichen werden:

- Anzahl der `DiscoveredCandidate`,
- Reihenfolge,
- URI,
- Label,
- Score,
- Match-Status,
- Entity Type,
- Candidate Provider URI,
- Metadata.

Der Test verwendet eine kontrollierte minimale Provider-Konfiguration und darf nicht von einer externen API abhängen.

**Erwartete Ausgabe**

```text
WP3-001-005 Discovery Runtime Regression: OK
```

---

# 10. Anpassung bestehender Tests

## RuntimeFactoryTest

**Datei**

```text
tests/Factory/RuntimeFactoryTest.php
```

Der bestehende Test bleibt erhalten und wird nur angepasst, falls die neue interne Verdrahtung zusätzliche Vocabulary-Testdaten erfordert.

Die bisherigen Aussagen bleiben gültig:

- `RuntimeFactory` implementiert `ApplicationServiceFactory`,
- jede Processing Unit erhält einen neuen Application Service.

## ReconciliationCompositionRootTest

Der Test bleibt fachlich unverändert.

Er muss weiterhin nachweisen, dass der Composition Root einen `ReconciliationController` erzeugt.

Die bekannte Abhängigkeit von einer lokalen `config/database.php` bleibt außerhalb des Scopes von WP3-001-005.

---

# 11. Implementierungsreihenfolge

1. `LegacyDiscoveryCompatibilityAdapter` implementieren.
2. Unit Test des Compatibility Adapters ergänzen.
3. Übergangsrouten in `RuntimeFactory` erzeugen.
4. `CandidateDiscoveryAdapterRegistry` mit `legacy-discovery` aufbauen.
5. `DiscoveryConfigurationRegistry` mit Root- und SubVocabulary-Routen aufbauen.
6. `CandidateDiscoveryRouter` erzeugen.
7. Router an den `ReconciliationApplicationService` übergeben.
8. Runtime-Integrationstest ergänzen.
9. Regressionstest alter gegen neuen Discovery-Pfad ergänzen.
10. Bestehende Tests ausführen.

---

# 12. Geplanter Änderungsumfang

## Neue Produktionsdatei

```text
src/Infrastructure/Discovery/Adapter/LegacyDiscoveryCompatibilityAdapter.php
```

## Geänderte Produktionsdatei

```text
src/Infrastructure/Factory/RuntimeFactory.php
```

## Voraussichtlich unveränderte Produktionsdateien

```text
src/Infrastructure/Composition/ReconciliationCompositionRoot.php
src/Infrastructure/Reconciliation/LegacyCandidateDiscoveryAdapter.php
src/Infrastructure/Runtime/RuntimeContext.php
src/Application/Reconciliation/ReconciliationApplicationService.php
src/Application/Reconciliation/CandidateDiscoveryGateway.php
```

## Neue Testdateien

```text
tests/Infrastructure/Discovery/Adapter/LegacyDiscoveryCompatibilityAdapterTest.php
tests/Factory/RuntimeFactoryDiscoveryIntegrationTest.php
tests/Integration/DiscoveryRuntimeRegressionTest.php
```

## Möglicherweise angepasste Testdatei

```text
tests/Factory/RuntimeFactoryTest.php
```

---

# 13. Akzeptanzkriterien

WP3-001-005 ist umgesetzt, wenn:

1. `LegacyDiscoveryCompatibilityAdapter` den `CandidateDiscoveryAdapter` implementiert.
2. Der Compatibility Adapter den bestehenden Legacy-Pfad unverändert delegiert.
3. `RuntimeFactory` Root- und SubVocabulary-Routen erzeugt.
4. Jede Route den Adapter-Key `legacy-discovery` verwendet.
5. `CandidateDiscoveryAdapterRegistry` den Compatibility Adapter bereitstellt.
6. `DiscoveryConfigurationRegistry` die Übergangsrouten bereitstellt.
7. `CandidateDiscoveryRouter` als `CandidateDiscoveryGateway` an den Application Service übergeben wird.
8. Der Application Service nicht mehr direkt mit `LegacyCandidateDiscoveryAdapter` verbunden ist.
9. Der neue und der bisherige Discovery-Pfad identische `DiscoveredCandidate[]` liefern.
10. Tenant-gebundene Adapterinstanzen nicht zwischen Processing Units geteilt werden.
11. Die Tests aus WP3-001-001 bis WP3-001-004 weiterhin erfolgreich sind.
12. Alle neuen Unit-, Integrations- und Regressionstests erfolgreich sind.
13. Alle übrigen ausführbaren Bestandstests weiterhin erfolgreich sind.

---

# 14. Out of Scope

Nicht Bestandteil sind:

- produktiver `LocalReconciliationStoreAdapter`,
- produktiver `XtreeJsonApiAdapter`,
- Entfernung des bestehenden `LegacyCandidateDiscoveryAdapter`,
- Umbau des `ReconciliationOrchestrator`,
- Umbau der `ProviderRegistry`,
- neue Discovery Methods,
- Prioritäten oder Fallback-Routen,
- mehrere gleichzeitig aktive Routes pro Scope,
- Hybrid Discovery,
- HTTP-, REST- oder SPARQL-Änderungen,
- Änderungen am Domain Model,
- Änderungen am Persistence Model,
- Änderungen an Tenant-Regeln.

---

# 15. Risikoanalyse

## R1 – Fehlende Route für bestehende Vocabulary-Anfragen

**Risiko**

Der Router würde vor Erreichen des Legacy-Pfads mit einer `DiscoveryConfigurationException` abbrechen.

**Gegenmaßnahme**

Automatische Erzeugung einer Route für:

- jedes konfigurierte Root Vocabulary,
- jedes konfigurierte SubVocabulary.

## R2 – Mehrere aktive Übergangsrouten pro Scope

**Risiko**

Der Router würde eine `AmbiguousDiscoveryRouteException` auslösen.

**Gegenmaßnahme**

WP3-001-005 erzeugt pro Scope exakt eine Compatibility Route.

## R3 – Verlust der Tenant-Isolation

**Risiko**

Ein tenant-gebundener Legacy Adapter könnte zwischen Processing Units geteilt werden.

**Gegenmaßnahme**

Compatibility Adapter und Adapter Registry werden innerhalb von `RuntimeFactory::create()` pro Tenant neu erzeugt.

## R4 – Unbeabsichtigte Änderung der Candidate-Ergebnisse

**Risiko**

Die zusätzliche Framework-Schicht verändert Reihenfolge oder Inhalt.

**Gegenmaßnahme**

Compatibility Adapter delegiert ohne Mapping. Ein Regressionstest vergleicht alten und neuen Pfad feldweise.

---

# 16. Engineering-Votum

Der vorgeschlagene Integrationsschnitt ist bewusst konservativ:

- eine neue Compatibility-Adapterklasse,
- eine gezielte Änderung in `RuntimeFactory`,
- bestehender Legacy Adapter bleibt unverändert,
- bestehender Orchestrator und Provider-Pfad bleiben unverändert,
- Routes und Registries werden tenant-spezifisch im bestehenden Runtime-Lifecycle erzeugt.

Damit wird das Discovery Framework produktiv eingehängt, ohne WP3-001-005 mit der späteren Neuimplementierung des Local Reconciliation Store oder der xTree API zu vermischen.
