Project-Offer-Sync-Hygiene — URL-Kanon, Title-Fallback, Outbound-Field-Binding, Sitemap-IA
accepted
solcom_project_offer): (1) Kanonische URL /projektpartnerportal. (2) Kein null_coalesce-Fallback auf SF-IDs für Pflicht-Felder — skip_on_empty(method: row) + SOQL-Filter. (3) Canvas-Bindings an outbound-only Felder nur mit optionalem SDC-Schema + Twig-{% if %}-Guard. (4) Menü-Struktur folgt der Sitemap-IA (docs/context/0002-sitemap.html) — keine parallelen Top-Level-Items für IA-Sub-Knoten.
Kontext
Die Fix-Iteration auf feature/P2503-108 (2026-05-15) hat fünf Drift-Klassen aufgedeckt, die durch das Zusammenspiel von migrate_plus, Pathauto, Canvas-Templates und manueller Menü-Pflege entstanden sind:
- URL-Drift: Plan/PRD-Drafts (2026-05-13/14) führten
/projektportal/projektangebote/als Pfadschema; CONTEXT.md + ADR-0031 wurden später auf/projektpartnerportalaktualisiert. Code spaltete sich auf: View an/projektportal, Pathauto-Pattern an/projektpartnerportal/[node:title], Canvas-Buttons an/projektpartnerportal. - Title-Fallback auf SF-ID:
process.title: { plugin: null_coalesce, source: [TR1__Job_Title__c, Id] }. Records ohne Job_Title bekamen die SF-ID als Titel, Pathauto sluggifizierte das zu/a1wau000001ylat2au. - Canvas-Binding an outbound-only Felder: Das Canvas-Template
node.project_offer.fullbindetsticky-apply-bar.apply_urlanfield_sf_application_url— laut Migrate-Kommentar „outbound-only" und damit NIE durch den Sync gefüllt. Ergebnis: Twig-Runtime-Errors auf jeder Detail-Seite. - High-Water-Fallstrick:
migrate_plus'high_water_property: LastModifiedDatehielt die letzte Sync-Marke fest. 219 SF-Records waren am Source, nur 5 mitLastModifiedDate > high_waterwurden importiert — kein Watchdog-Eintrag, weil High-Water-Skips silent passieren. - Sitemap-IA-Verletzung in der Planung: Ein erster Plan-Entwurf wollte „Projektpartnerportal" als neuen Top-Level-Eintrag ins Main-Menü hängen.
docs/context/0002-sitemap.htmldefiniert die View aber explizit als N16 Projektliste — Sub-Knoten unter dem bestehenden Top-Level N14 „Für Freiberufler".
Jede einzelne dieser fünf Klassen ist mehrfach in der Doku-Strange dokumentiert, aber bisher in keinem ADR konsolidiert. Dieser ADR macht die Regeln für künftige Migrate-Sync-Module verbindlich.
Entscheidung
1) URL-Kanon: /projektpartnerportal superseded by ADR-0033
Supersede-Note (2026-05-25): Diese Regel ist durch ADR-0033 ersetzt. Der kanonische Pfad ist /fuer-freiberufler/projektliste/<slug> (Sitemap-Node N16 unter N14, Context-0002); /projektpartnerportal/ bleibt als Legacy-301-Redirect. ADR-0033 enthält Begründung, Hook-Strategie und Idempotenz-Garantien. Die übrigen Regeln R2–R4 unten bleiben in Kraft.
Original-Wortlaut (zur Nachvollziehbarkeit): Listing-View und Pathauto-Pattern für project_offer-Nodes liegen unter /projektpartnerportal. CONTEXT.md und docs/adr/0031-sf-publication-frontend-decomposition.html sind die Wahrheitsanker. Ältere PRD/Plan-Drafts (2026-05-13 PRD, 2026-05-14 Plan) mit /projektportal/projektangebote/ sind superseded; Inline-Worklogs in diesen Dokumenten markieren das.
2) Migrate-Title-Pflicht: kein ID-Fallback
Records ohne den Pflicht-Title-Source-Wert werden nicht zu Drupal-Nodes. Verbindliches Migrate-Pattern (belt-and-suspenders):
source:
urls:
- "...?q=...+WHERE+RecordType.DeveloperName='Publication'+AND+TR1__Job_Title__c+!%3D+null"
process:
title:
-
plugin: skip_on_empty
method: row
source: TR1__Job_Title__c
-
plugin: get
source: TR1__Job_Title__c
Kein null_coalesce mit einer ID-artigen Quelle als Fallback. Das produziert unbenutzbare Nodes mit der SF-ID als Titel und damit als Pathauto-Slug.
3) Outbound-Field-Binding-Disziplin
Canvas-Content-Templates dürfen Felder, die ausschließlich outbound beschrieben werden (z. B. field_sf_application_url via Writeback-Queue), nur mit zwei Schutzlagen binden:
- Das SDC-Schema deklariert das Prop als optional und nullable (
type: ["string", "null"], keinrequired:-Eintrag). - Das Twig-Template kapselt den Render-Body in einen
{% if <prop> is not empty %}…{% endif %}-Guard.
Outbound-Felder werden in docs/context/0008-fcsb-bh4sf-publication-mapping.html markiert; jedes neue Canvas-Template muss dieses Mapping konsultieren, bevor es ein Field bindet.
4) Menü-Struktur folgt der Sitemap-IA
docs/context/0002-sitemap.html ist die Wahrheits-Vorlage für Top-Level- und Sub-Knoten der Main-Navigation. Neue Routes werden als Sub-Item unter dem passenden Strang-Top-Level (N07, N14, N22, …) angelegt, nicht als paralleler Top-Level-Eintrag. Konkrete Anwendung: /projektpartnerportal ist N16 „Projektliste" unter N14 „Für Freiberufler". (N14 wurde im Zuge dieser ADR von „Für Freiberufler / Projektportal" auf „Für Freiberufler" gekürzt, da das URL-Anliegen jetzt vom Sub-Item getragen wird.)
Verbindlichkeit
Diese Regeln gelten für solcom_project_offer (initialer Anwender) und alle künftigen Migrate-Sync-Module (Personio-Feeds, Freelancer-Plattformen). Diff-time-Reviewer (skills) prüfen sie mit:
drupal-config-architecture-reviewer: keinnull_coalesce-Fallback aufId-artige Source-Felder improcess.title.drupal-twig-sdc-reviewer: Canvas-Bindings an Felder aus der Outbound-Liste (Context-0008) brauchenrequired:-Removal + Twig-Guard.
Rejected alternatives
| Alternative | Warum verworfen |
|---|---|
| Schema relaxen ohne Twig-Guard | Versteckt das Symptom auf SDC-Ebene, fliegt beim ersten Renderer-Wechsel wieder auf. Kein redundanter Sicherheitsgurt — wenn das Schema-Tooling versagt, fehlt der Fallback. |
Nur SOQL-Filter, kein skip_on_empty |
Single point of failure. SOQL-Cache, Index-Drift oder ein versehentlich entfernter WHERE-Teil reichen aus, um NULL-Titel wieder ins Drupal zu kippen. |
| Neuer Top-Level-Eintrag „Projektpartnerportal" | Verletzt die Sitemap-IA. N14 (Strang 2) ist bereits der Anker für Freelancer-Themen — ein paralleler Top-Level fragmentiert den Strang und macht den User-Pfad unklarer. |
null_coalesce(..., Id) als Migrate-Komfort |
Produziert unbenutzbare Drupal-Nodes (SF-ID als sluggifizierter Titel und Alias), und weil das technisch ein Success ist, schweigt migrate:messages. Defekt bleibt unsichtbar. |
Consequences
- Test-Erweiterung: Migrate-Kernel-Tests im
solcom_project_offer-Modul müssen einen Test-Fall „Source-Row ohneTR1__Job_Title__c" enthalten, der nachweist, dass kein Node entsteht. - Re-Import-Hygiene: Bei Code-Änderungen am Migrate-Pipeline-Prozess muss vor dem nächsten
drush mimder High-Water-Marker explizit gelöscht werden (\Drupal::keyValue('migrate:high_water')->delete('solcom_project_offer')) — sonst werden ältere Records still übersprungen. Dieser Hinweis wandert in dieREADME.mddes Moduls. - Reviewer-Agents: Die zwei oben genannten Diff-time-Reviewer bekommen einen neuen Rule-Check; die Skill-Definitionen unter
.claude/skills/drupal-config-workflow/und.claude/skills/drupal-twig-sdc-authoring/referenzieren diese ADR als Source-of-truth. - IA-Folge-Aufgabe: Die Konsolidierung des Strang-2-Teilbaums (N15, N17, N19 als Sub-Items unter N14) ist nicht in dieser Iteration enthalten — die Canvas-Pages
page/14(Mein Projektpartnerprofil),page/16(Projekt-Alert) und das Referenzen-Item liegen heute als Top-Level oder mit falschem Parent in der DB. Ein eigener Restructuring-PR mussdocs/context/0002-sitemap.htmlals Soll-Vorlage abarbeiten. - Pathauto-Bulk: Nach jedem Title-Pipeline-Change muss
drush pathauto:aliases-generate update canonical_entities:node --no-interactionlaufen, damit Aliase mit dem aktuellen Pattern neu generiert werden. Stale Aliase aus pre-Rename-Migrates müssen manuell weggeräumt werden.