solcom / projektpartnerportal-figma-alignment · PR-Review · 2026-05-17 · fixed-point: origin/develop

PR #164354 — Projektpartnerportal Listing & Vacancy-Fixes (Wave A–D)

MP
Marc Philipps
36 Commits · PR #164354
feature/projektpartnerportal-figma-alignment develop
+2270 / −101550 files
Findings: standards=10 (7 hard, 3 judgement) · canvas=4 (3 hard, 1 judgement) · spec=6 (1 missing-deferred, 4 scope-creep, 1 plan-drift)
Worst: result-counter.component.yml trägt status: experimental, ist aber in canvas.component.sdc.byte_theme.result-counter.yml als Production-Component registriert — blockiert Canvas-Eligibility und kann je nach Canvas-Version den Editor-Pickup verhindern.

Risk map

safe worth a look needs attention

Standards

Hardcoded Brand-Hex statt CSS-Variable — diverse Twig-/Theme-Files Hard Violation
HardKanonische Brand-Hex müssen über var(--brand-*) referenziert werden

watchlist-bar.twig:16: bg-[#011C53]/30 — Brand-Token-Wert ist #001F46 (--brand-dark-blue), nicht #011C53. Entweder Token-Verwendung oder Begründung warum ein anderer Hex.

bef-checkboxes.html.twig:50–51: bg-[#BEDC00] und border-[#BEDC00] — sollte bg-[var(--brand-lime)].

views-exposed-form--project-offers-listing--block-listing.html.twig:23: bg-[#001F46]/30bg-[var(--brand-dark-blue)]/30.

select--edit-branche.html.twig:16,29, input--textfield--edit-einsatzort.html.twig:16,23, pager--project-offers-listing--block-listing.html.twig:77, byte_theme.theme:174: mehrfach text-[#001F46]. input--textfield… + select--edit-branche… nutzen zusätzlich text-[#828282] / placeholder-[#828282] (--brand-gray).

card-bubble-profile.twig:13: border-[#0050A0] — keine bestehende Brand-Token-Entsprechung; entweder Token anlegen oder Verwendung begründen.

Quelle: .claude/agents/solcom-brand-reviewer.md ↔ .claude/skills/solcom-styleguide/SKILL.md
card-bubble-profile.twig:39 — Inline-Ternary direkt in class-Attribut Hard Violation
HardTwig-Ternary innerhalb HTML-Tag-Attribut verboten

Diff zeigt <div class="{{ is_listing ? 'bubble-card__cta mt-6' : 'bubble-card__cta-pill' }}">. AGENTS.md Rule 1/6: „Never use inline conditionals within HTML tag attributes." Vorab via {% set cta_class = is_listing ? '…' : '…' %} oder per CVA berechnen.

Quelle: web/themes/custom/byte_theme/AGENTS.md — Rule 1 / Rule 6
bef-checkboxes.html.twig:60 — Include ohne with … only bzw. with_context: false Hard Violation
HardFehlende Context-Isolation beim contrib-Template-Include

{% include '@better_exposed_filters/bef-nested-elements.html.twig' %} ohne dritten Parameter. AGENTS.md Rule 11 fordert explizit with_context: false oder with { … } only, um Context-Pollution zu vermeiden.

Quelle: web/themes/custom/byte_theme/AGENTS.md — Rule 11 „Always Use with_context: false or with only"
card-bubble-profile — neue listing-Variante (Bubble → Rechteck) ohne ADR Judgement Call
JudgementStrukturelle Topologie-Änderung ohne dokumentierte Decision

Die neue Variante ändert die Geometrie der SDC strukturell (Bubble-Notch vs. Listing-Rectangle). drupal-config-architecture-reviewer.md verlangt bei Topologie-Decisions mit ≥2 Optionen Customer-Lens-Konsultation + ADR. Plan 0001 §Decisions argumentiert die Variante zwar fachlich, aber kein ADR-File reflektiert die Entscheidung — der Plan ist „proposed", keine entscheidungs-tragende ADR.

Quelle: .claude/agents/drupal-config-architecture-reviewer.md; docs/plans/0001-projektpartnerportal-listing-figma-alignment.html §Decisions
watchlist-bar.twig — Variant-Branching per {% if resolved_variant == 'top' %}…{% else %}…{% endif %} Judgement Call
JudgementDuplikat-Split statt CVA-gesteuerter Klassen-Variante

Die Variante-Unterscheidung wickelt zwei nahezu vollständige <aside>-Bäume in ein if/else. AGENTS.md Rule 9 empfiehlt CVA-Klassen-Resolution; Komplett-Markup-Splits sind nicht explizit verboten, aber Borderline. Falls die beiden Varianten strukturell genug abweichen, ist der Split vertretbar — Entscheidung explizit dokumentieren.

Quelle: web/themes/custom/byte_theme/AGENTS.md — Rule 9
byte_theme.theme — neue byte_theme_preprocess_bef_links-Funktion prozedural Judgement Call
JudgementHook in .theme statt in ThemeHooks.php (Attribute-Style)

Drupal 11.3+ unterstützt Attribute-basierte Hooks (siehe src/Hook/ThemeHooks.php). Die neue Funktion ist im klassischen .theme-Stil — funktional korrekt, aber inkonsistent mit dem bereits etablierten ThemeHooks-Pattern.

Quelle: .claude/agents/drupal-php-reviewer.md; .claude/skills/drupal-module-development/SKILL.md
Notes (geprüft & OK) Skip-confirmations

DurationBucket.php nutzt #[ViewsFilter(...)]-PHP-Attribut — Drupal-11-Attribute-Syntax korrekt.

solcom_vacancy_update_9003 ist ein neuer Update-Hook (kein Schema-Konflikt mit 9002).

build/main.min.css ist committed — konsistent mit AGENTS.md Rule 13 (Tailwind-Build artefact).

watchlist-bar.twig nutzt with_context: false für @byte_theme/components/{icon,button}/…-Includes korrekt.

field_matching_score neu in config/sync/ — Field-Storage + Field-Config beide vorhanden, kein hook_install nötig (config-only).

Canvas-Korrektheit

result-counter.component.yml:4 — status: experimental auf Production-registrierter SDC Hard Violation
HardCanvas-Eligibility blockiert durch experimental-Status

result-counter.component.yml:4: status: experimental. Demgegenüber canvas.component.sdc.byte_theme.result-counter.yml:3: status: true — die SDC ist in Canvas aktiviert, das Theme-Schema flagged sie aber als experimental. drupal-canvas-Skill: Canvas-eligible SDCs müssen mindestens stable-Status haben, sonst kann der Page-Builder-Editor sie in einigen Canvas-Versionen nicht zur Auswahl anbieten.

Quelle: .claude/skills/drupal-canvas/SKILL.md (Canvas-Eligibility), .claude/agents/drupal-twig-sdc-reviewer.md
SDC-Versions-Drift zwischen active_version und älteren Snapshots Hard Violation
HardSnapshot-Inkonsistenz in result-counter + watchlist-bar

canvas.component.sdc.byte_theme.result-counter.yml registriert zwei Snapshot-Keys (780335f527bb637c als active plus älteren 0d0e469fa1c1ee09) mit identischen Props — Drift-Hinweis nach einem Reset.

canvas.component.sdc.byte_theme.watchlist-bar.yml: alter Snapshot 308cfd5169ffe2ee kennt nur max_entries und apply_all_url; der active-Snapshot hat zusätzlich variant. Fällt Canvas auf den alten Snapshot zurück, fehlt variant im Editor — obwohl der Twig sie bereits erwartet.

Empfehlung: alte Snapshots prunen oder verifizieren, dass das Theme-Build-Hash zur active_version passt (vor Deploy: drush ev "print_r(\Drupal::service('canvas.component_versions')->getHashes());").

Quelle: .claude/skills/drupal-canvas/SKILL.md §Versions
watchlist-bar.component.yml — variant-Prop ohne examples Hard Violation
HardCanvas-Editor erhält keinen Seed-Wert

Im selben File ist max_entries mit examples: [5] versorgt, variant bleibt ohne examples. drupal-canvas-SKILL.md verlangt für jede Prop einen examples-Eintrag (erstes Beispiel = Default für den Editor). Inkonsistent innerhalb derselben Datei.

Quelle: .claude/skills/drupal-canvas/SKILL.md §Canvas-Eligibility (examples-Pflicht)
project_offer.card.yml / vacancy.card.yml — matching_score: 0 als Static-Default Judgement Call
JudgementDeferred-Binding-Risk für künftigen Scoring-Service

Beide Card-Templates setzen matching_score: 0 als statischen Input — die Pill rendert dauerhaft im opacity-70-State. Wenn Plan S-11 nur ein Stub ist und der Score später aus einem Scoring-Dienst kommt, muss der Input auf sourceType: entity-field umgestellt werden. Heute kein Fehler, aber leicht zu übersehen beim Service-Bind.

Quelle: Canvas-Sub-Agent-Read; docs/plans/0001-…html S-11
Notes (geprüft & OK) Skip-confirmations

Breadcrumb-Removal-Tree-Konsistenz: in allen vier .full-Templates (blog, page, project_offer, vacancy) sind die entfernten block.system_breadcrumb_block-Knoten Top-Level-Roots; keine verbleibenden Kinder mit dangling parent_uuid.

Views-Exposed-Filter-Block-Wrapper (canvas.component.block.views_exposed_filter_block.project_offers_listing-block_listing.yml): Provider views korrekt, Dependency auf views.view.project_offers_listing gesetzt, slot_definitions: null für Block-Components valide.

Canvas-Folder (5e3c80cd-…): referenziert ausschließlich Component-IDs, die im selben Diff registriert sind — kein dangling Reference.

project_offer.card.yml Field-Bindings: B (field_sf_industry), C (field_sf_job_title), D1 field_sf_duration, D2 field_sf_estimated_start, D4 field_sf_city, E (field_sf_long_description), plus Utilisation und Application-URL — alle als entity-field-Expression im kanonischen ℹ︎␜entity:…␞␟value-Pattern. D3 Remote-Anteil fehlt (Feld existiert nicht im Datenmodell, deferred via Plan §08).

card-bubble-profile Canvas-Registry: variant + matching_score im active-Snapshot vorhanden; SDC status: stable; Eligibility erfüllt. Alter Snapshot 283c56c0b873cb9b mit leeren Props bleibt aus Migrations-Gründen erhalten — strukturell korrekt.

Spec

project_offer.card.yml — Fields A–E gerendert; nur D3 (Remote) fehlt deferred Missing Requirement
GapPlan S-09 zu 5/6 erfüllt — Remote-Field-Binding nicht vorhanden

Korrektur 2026-05-17: Die Listing-Card hat sehr wohl die Slot-Children mit entity-field-Bindings — Branche (B, field_sf_industry), Heading (C, field_sf_job_title), D1 Dauer (field_sf_duration), D2 Start (field_sf_estimated_start), D4 City (field_sf_city) sowie zusätzlich Utilisation und Application-URL, plus Teaser (E, field_sf_long_description).

Offen ist D3 Remote-Anteil — kein field_sf_remote-Binding. Das entspricht dem aktuellen Datenmodell (Feld existiert nicht), Plan §08 Follow-up-Tabelle listet „Remote-Anteil" als Figma-gap. Nicht weiter zu adressieren in diesem PR.

Quelle: docs/plans/0001-…html S-09 §04 + §08 Follow-up; config/sync/canvas.content_template.node.project_offer.card.yml (verifiziert via grep, 7 Field-Bindings vorhanden)
Plan §04 S-10 driftet — bottom ist der neue Default Plan-Drift
NoteUser-Klarstellung 2026-05-17: watchlist-bar-Default bottom ist gewollt

Commit ba46209 fix(theme): watchlist-bar immer als Bottom-Variante rendern ist als bewusste Standard-Setzung bestätigt. Plan §04 S-10 „top-sticky als Default auf /projektpartnerportal" reflektiert die alte Designannahme und muss bei der nächsten Plan-Pflege auf bottom-Default umgeschrieben werden — Empfehlung in der Checkliste unten.

Quelle: User-Klarstellung 2026-05-17; Commit ba46209
Breadcrumb-Removal-Sweep auf 4 Content-Templates Scope Creep
Creepchore-Commit entfernt system_breadcrumb_block aus allen .full-Templates repo-weit

Commit 22d83f4 chore(canvas): Breadcrumb-Block aus allen .full Content-Templates entfernen trifft blog.full.yml, page.full.yml, project_offer.full.yml, vacancy.full.yml. Plan erwähnt Breadcrumbs nicht; commit-message argumentiert „bewusster Sweep", aber kein ADR / Plan-Entry deckt die UX-Entscheidung ab.

Quelle: Commit 22d83f4 ↔ docs/plans/0001-…html (kein Treffer für „breadcrumb")
ADR-0036 Canvas-Page Portability — Add + Revert + Wiki-Entity Scope Creep
CreepDrei Doku-Commits zur Canvas-Page-Portability-Diskussion ohne Plan-Bezug

Commits 8c0936c (ADR-0036 proposed), 1f90204 (Revert), db5d289 (Wiki recipe-apply-risk) gehören zur parallelen Canvas-Page-Portability-Debatte. Konsistent mit Memory project_canvas_page_local_curation (kein Recipe-Content). Inhaltlich legitim, aber gehört nicht in die Figma-Alignment-Branch.

Quelle: Commits 8c0936c / 1f90204 / db5d289; Memory project_canvas_page_local_curation
.claude/, AGENTS.md, docs/agents/* — Meta-Updates ohne Plan-Bezug Scope Creep
CreepAgent-/Skill-Housekeeping in Feature-Branch

Geänderte Meta-Files: .claude/settings.json, .claude/skills/{figma-autonomous-implementer,solcom-build-frontend}/SKILL.md, .claude/agents/solcom-figma-reviewer.md, AGENTS.md, docs/agents/{reviewers,sandcastle,skills}.html, docs/index.html, docs/LOG.html, docs/plans/2026-05-14-001-feat-project-offer-sync-bh4sf.html, docs/prds/2026-05-13-salesforce-publication-sync-prd.html, zwei ADRs (0012, 0014), docs/design/MAPPING.html.

Plan/PR-Beschreibung deckt diese Housekeeping-Commits nicht. Stört nicht funktional, aber bläht den PR auf 50 Files auf — Splitting in Feature-PR + Housekeeping-PR hätte den Review beschleunigt.

Quelle: git diff origin/develop...HEAD --name-only
Slices 13–20 — Commit-Labels ohne Plan-Definition Missing Requirement
GapPlan definiert Slices S-01 bis S-12; Commits label sich aber „Slice 13" bis „Slice 20"

Commit-Labels: Slice 13 (bef_links-Laufzeit), 14 (Watchlist-CSS-Override), 15 (Branche-Checkboxes Custom-Style), 16 (Watchlist-Bar Top-Variant Fix), 17 (result-counter Umlaut), 18 (Branche-Select-Dropdown), 19/20 (Pill-Styling). Plan §04 Summary-Box: „12 Slices".

Diese acht Iterationen sind sinnvoll, aber im Plan nicht als eigene AKs nachgepflegt. Plan-Sektion „Follow-up / Open Gaps (post-Wave-D, 2026-05-17)" listet einige Punkte — eine Pflege-Iteration für die Slice-Nummerierung steht aus.

Quelle: git log origin/develop..HEAD ↔ docs/plans/0001-…html §04
S-12 Visual-Verify Pass Desktop 1280 — Ergebnis nicht im Diff dokumentiert Missing Requirement
GapPlan AK „Lighthouse Performance ≥ 75, Accessibility ≥ 90, 0 JS-Fehler" unbelegt

Plan-Sektion 04 S-12 + §06 Verifikation: Visual-Verify-Pass mit dokumentiertem Side-by-Side gegen Figma-Frame 985-50024 und Lighthouse-Scores. Diff enthält keinen Eintrag in docs/reports/ oder Screenshots — der Pass fehlt entweder oder lief nur lokal ohne Persistenz (verletzt Memory feedback_visual_verify_side_by_side + feedback_solcom_review_persist_default).

Quelle: docs/plans/0001-…html S-12 §04 + §06; Memory feedback_visual_verify_side_by_side
Plan-Slices S-01, S-02–S-06, S-07, S-08, S-11 — vollständig ausgeliefert Notes

S-01 Canvas-Page-Chassis /projektpartnerportal — Route + Slots + Views-Block ✓ (Commit e1dcd58).

S-02–S-05 BEF-Form + Widget-Overrides (Search, Select, Checkboxes) — Twig-Templates neu unter templates/better_exposed_filters/ + templates/form/ ✓.

S-06 Custom-BEF-Plugin DurationBucket — neu unter solcom_project_offer/src/Plugin/views/filter/ ✓.

S-07 Result-Counter-SDC + Canvas-Component-Registrierung ✓ (Status-Tag-Fix steht aus, siehe Standards).

S-08 Pagination-Twig-Override pager--project-offers-listing--block-listing.html.twig ✓.

S-11 Matching-Score-Field-Stub — field.storage.node.field_matching_score.yml + field.field.node.project_offer.field_matching_score.yml ✓.