From f136bec0d7b62a6726eb00564f7b4d96663d58de Mon Sep 17 00:00:00 2001 From: team 1 Date: Thu, 7 May 2026 14:42:12 +0200 Subject: [PATCH] p59 --- config/retriex/genre.yaml | 152 ++++-------------- ..._GENRE_NATIVE_SOURCEPATH_CLEANUP_README.md | 109 +++++++++++++ public/assets/styles/base.css | 6 +- src/Config/GenreSourceOfTruthGuard.php | 55 ++++++- 4 files changed, 193 insertions(+), 129 deletions(-) create mode 100644 patch_history/RETRIEX_PATCH_59A_GENRE_NATIVE_SOURCEPATH_CLEANUP_README.md diff --git a/config/retriex/genre.yaml b/config/retriex/genre.yaml index 3b4c504..485fee7 100644 --- a/config/retriex/genre.yaml +++ b/config/retriex/genre.yaml @@ -187,8 +187,7 @@ parameters: product_roles: description: Current water-analysis product role vocabulary. For another genre, replace these terms with that genre's main-product, accessory and consumable roles. primary_product_terms: - source_paths: - - vocabulary.classes.device + origin: genre_native terms: - analysegerät - analysegeraet @@ -214,8 +213,7 @@ parameters: - anlage - anlagen accessory_product_terms: - source_paths: - - vocabulary.classes.accessory + origin: genre_native terms: - zubehör - zubehor @@ -239,8 +237,7 @@ parameters: - serviceset - service-set requested_accessory_code_terms: - source_paths: - - vocabulary.classes.requested_accessory_code_terms + origin: genre_native terms: - indikatortyp - indikator @@ -248,13 +245,7 @@ parameters: - reagenz - reagent shop_views: - source_paths: - - vocabulary.views.shop.device_query.add - - vocabulary.views.shop.accessory_query.add - - vocabulary.views.shop.device_product.add - - vocabulary.views.shop.accessory_product.add - - vocabulary.views.shop.device_focus.add - - vocabulary.views.shop.accessory_focus.add + origin: genre_native device_query_terms: - analysegerät - analysegeraet @@ -420,11 +411,7 @@ parameters: - kalibrierlösung - kalibrierloesung prompt_views: - source_paths: - - vocabulary.views.prompt.main_device_request_keywords.add - - vocabulary.views.prompt.accessory_request_keywords.add - - vocabulary.views.prompt.main_device_product_keywords.add - - vocabulary.views.prompt.accessory_product_keywords.add + origin: genre_native main_device_request_keywords: - messanlage - messanlagen @@ -531,8 +518,6 @@ parameters: - ph indikatoren no_llm_fallback_terms: source_paths: - - vocabulary.classes.agent_no_llm_main_device_request_keywords - - vocabulary.classes.agent_no_llm_accessory_product_keywords - agent.no_llm_fallback.product_roles.vocabulary_views.main_device_request_keywords - agent.no_llm_fallback.product_roles.vocabulary_views.accessory_product_keywords main_device_request_keywords: @@ -585,12 +570,8 @@ parameters: description: Current genre attributes and constraint terms. Fashion would typically replace these with size, color, material, fit and variant constraints. direct_attribute_cleanup: source_paths: - - vocabulary.classes.direct_product_attribute_stop_terms - - vocabulary.views.search_repair.direct_product_type_terms.add - - vocabulary.views.search_repair.direct_product_attribute_stop_terms.include - agent.shop_runtime.attribute_cleanup.vocabulary_views.product_type_terms - agent.shop_runtime.attribute_cleanup.vocabulary_views.stop_terms - - agent.shop_runtime.attribute_cleanup.comparative_constraint_patterns product_type_terms: - anschlusskabel - kabel @@ -653,9 +634,6 @@ parameters: - /\b(?:länger|laenger|kürzer|kuerzer|größer|groesser|kleiner|über|ueber|unter|mindestens|maximal|maximum|minimum|ab|bis|mehr\s+als|weniger\s+als)\s+(?P\d+(?:[,.]\d+)?\s*[\p{L}µ°%]*)\b/iu size_and_color_terms: source_paths: - - intent.commerce.size_token_terms - - intent.commerce.size_terms - - intent.commerce.color_terms - intent.commerce.patterns.size_extraction_template - intent.commerce.patterns.size_value_template - intent.commerce.patterns.size_token_value_template @@ -716,15 +694,13 @@ parameters: brands_and_canonical_terms: description: Genre-specific brands, canonical tokens and query enrichment rules. known_brands: - source_paths: - - commerce_query.known_brands + origin: genre_native terms: - heyl - horiba - neomeris canonical_terms: - source_paths: - - commerce_query.search_token_canonical_map + origin: genre_native map: indikatoren: indikator indicators: indikator @@ -734,8 +710,7 @@ parameters: reagent: reagenz produkte: produkt query_enrichment_rules: - source_paths: - - query_enrichment.rules + origin: genre_native rules: Wasserhärte: Resthärte Gerät: Modell @@ -749,8 +724,7 @@ parameters: store: shop Indikatortyp: Indikator accessory_focus_variants: - source_paths: - - vocabulary.maps.shop.accessory_focus_variants + origin: genre_native map: values: indicator_terms: @@ -777,8 +751,7 @@ parameters: serviceset: service_set_terms service-set: service_set_terms rag_evidence_synonyms: - source_paths: - - vocabulary.maps.agent.rag_evidence_guard.synonyms + origin: genre_native map: values: salinity_terms: @@ -815,7 +788,6 @@ parameters: description: Genre-specific words and patterns that steer commerce/advisory routing. fuzzy_routing_terms: source_paths: - - vocabulary.classes.input_normalization_fuzzy_routing_terms - agent.input_normalization.fuzzy_routing.vocabulary_views.terms terms: - shop @@ -886,10 +858,6 @@ parameters: - empfiehl commerce_intent: source_paths: - - intent.commerce.strong_signals - - intent.commerce.advisory_signals - - intent.commerce.advisory_product_selection_patterns - - intent.commerce.explicit_commerce_intent_patterns - intent.commerce.patterns.model_like_product strong_signals: - shop @@ -1057,8 +1025,7 @@ parameters: query_template_with_model: '{model} indikator' query_template_without_model: indikator referential_terms: - source_paths: - - agent.shop_runtime.context_resolution.context_usage.referential_terms + origin: genre_native terms: - der - die @@ -1083,10 +1050,7 @@ parameters: - selben history_anchor_enrichment: source_paths: - - vocabulary.classes.agent_shop_context_anchor_trigger_terms - agent.shop_runtime.context_resolution.history_anchor_enrichment.vocabulary_views.trigger_terms - - agent.shop_runtime.context_resolution.history_anchor_enrichment.anchor_patterns - - agent.shop_runtime.context_resolution.history_anchor_enrichment.template trigger_terms: - indikator - indikatortyp @@ -1102,9 +1066,7 @@ parameters: template: '{anchor} {query}' max_query_terms: 2 meta_query_guard: - source_paths: - - agent.shop_runtime.context_resolution.meta_query_guard.meta_only_terms - - agent.shop_runtime.context_resolution.meta_query_guard.context_fallback_filter_terms + origin: genre_native meta_only_terms: - shop - preis @@ -1129,11 +1091,7 @@ parameters: - messen - gemessen rag_anchor_enrichment: - source_paths: - - agent.shop_runtime.context_resolution.rag_anchor_enrichment.numeric_focus_patterns - - agent.shop_runtime.context_resolution.rag_anchor_enrichment.product_title_patterns - - agent.shop_runtime.context_resolution.rag_anchor_enrichment.anchor_bonus_patterns - - agent.shop_runtime.context_resolution.rag_anchor_enrichment.subject_terms + origin: genre_native numeric_focus_patterns: - /(?P\d+(?:[,.]\d+)?)\s*(?P°?\s*d\s*h|dh|dH)/iu product_title_patterns: @@ -1156,7 +1114,6 @@ parameters: description: Current direct Shopware query cleanup and deterministic answer wording for this genre. current_input_preservation_terms: source_paths: - - vocabulary.classes.agent_shop_current_input_preservation_terms - agent.shop_runtime.query_cleanup.current_input_preservation.vocabulary_views.terms terms: - ph @@ -1167,8 +1124,7 @@ parameters: - orp - 0,02 stopword_cleanup: - source_paths: - - agent.shop_runtime.query_cleanup.stopword_cleanup.terms + origin: genre_native terms: - zeige - zeig @@ -1212,14 +1168,12 @@ parameters: - meter - metern compound_prefix_match: - source_paths: - - agent.shop_runtime.result_identity.compound_prefix_match.terms + origin: genre_native terms: - puffer - kalibrierpuffer primary_identity_repair: - source_paths: - - agent.shop_runtime.result_identity.primary_identity_repair.stop_terms + origin: genre_native stop_terms: - messgerät - messgeraet @@ -1240,8 +1194,7 @@ parameters: - messkoffer - koffer semantic_shop_search_tokens: - source_paths: - - vocabulary.views.shop.semantic_search_tokens.add + origin: genre_native terms: - indikator - indicator @@ -1285,12 +1238,7 @@ parameters: - controller - system direct_answer: - source_paths: - - agent.shop_runtime.direct_answer.intro - - agent.shop_runtime.direct_answer.no_results - - agent.shop_runtime.direct_answer.sorted_by_length_note - - agent.shop_runtime.direct_answer.min_length_filter_note - - agent.shop_runtime.direct_answer.max_length_filter_note + origin: genre_native enabled: true max_results: 10 intro: 'Aus den Shopdaten ergeben sich folgende passende Treffer:' @@ -1301,12 +1249,7 @@ parameters: result_identity_and_answer_policy: description: Current role separation, fact-grounding and response-format rules that are genre-sensitive. prompt_rules: - source_paths: - - prompt.output_priority.technical_rules - - prompt.response_format.technical_rules - - prompt.response_format.accessory_rules - - prompt.fact_grounding.technical_rules - - prompt.fact_grounding.with_shop_rules + origin: genre_native output_priority_technical: - '- For technical questions, answer the exact requested fact first and keep it as the main answer.' - '- If one source chunk contains both the best matching value and nearby comparison values, use the nearby values only as context and do not include them unless the user asks for comparison or alternatives.' @@ -1394,8 +1337,7 @@ parameters: - '- If the user asks for the price or availability of a referenced accessory, indicator, reagent, kit, set, or consumable, use commercial fields only from a shop result that clearly matches that accessory identity and code.' - '- For such accessory price follow-ups, do not answer with the price, URL, product number, or availability of the main device or of unrelated reagents; if no matching accessory shop item is present, say that the price is not available in the provided shop data.' prompt_keyword_views: - source_paths: - - vocabulary.views.prompt.technical_product_keywords.add + origin: genre_native technical_product_keywords: - technisch - technical @@ -1443,11 +1385,7 @@ parameters: - chlor - chlormessung measurement_evidence_guard_terms: - source_paths: - - vocabulary.views.prompt.measurement_evidence_guard.accessory_lookup_guard_terms.add - - vocabulary.views.prompt.measurement_evidence_guard.accessory_lookup_passthrough_terms.add - - vocabulary.views.prompt.measurement_evidence_guard.generic_positive_context_terms.add - - vocabulary.views.prompt.measurement_evidence_guard.generic_negative_context_terms.add + origin: genre_native accessory_lookup_guard_terms: - indikator - indikatoren @@ -1493,10 +1431,7 @@ parameters: - störungsfrei - stoerungsfrei measurement_evidence_maps: - source_paths: - - vocabulary.maps.prompt.measurement_evidence_guard.request_terms - - vocabulary.maps.prompt.measurement_evidence_guard.positive_terms - - vocabulary.maps.prompt.measurement_evidence_guard.non_equivalent_terms + origin: genre_native request_terms: ph: - ph @@ -1543,20 +1478,17 @@ parameters: search_repair: description: Current search repair tokens, candidate patterns and exact identifier helpers. direct_product_attribute_lookup: - source_paths: - - search_repair.direct_product_attribute_lookup + origin: genre_native enabled: true min_query_tokens_after_cleanup: 2 comparative_constraint_patterns: - /\b(?:länger|laenger|kürzer|kuerzer|größer|groesser|kleiner|über|ueber|unter|mindestens|maximal|maximum|minimum|ab|bis|mehr\s+als|weniger\s+als)\s+(?P\d+(?:[,.]\d+)?\s*[\p{L}µ°%]*)\b/iu requested_accessory_code_terms: - source_paths: - - vocabulary.views.search_repair.requested_accessory_code_terms.include + origin: genre_native terms: - requested_accessory_code_terms candidate_patterns: source_paths: - - search_repair.specific_model_candidate_patterns - search_repair.patterns.model_candidate - search_repair.patterns.accessory_candidate_template - search_repair.patterns.requested_accessory_code @@ -1576,12 +1508,7 @@ parameters: whitespace_collapse: /\s+/u tokenize_cleanup: /[^\p{L}\p{N}\s\-]+/u candidate_terms: - source_paths: - - vocabulary.views.search_repair.model_candidate_exclude_terms.include - - vocabulary.views.search_repair.generic_candidate_tokens.add - - vocabulary.views.search_repair.accessory_candidate_terms.add - - vocabulary.views.search_repair.accessory_or_bundle_terms.add - - vocabulary.views.search_repair.specificity_boost_terms.add + origin: genre_native model_candidate_exclude_terms: - requested_accessory_code_terms - shop_meta_terms @@ -1632,8 +1559,7 @@ parameters: retrieval_and_language: description: Current protected terms, cleanup profiles and exact-selection helpers that are genre-sensitive. Retrieval engine parameters remain outside this area. protected_terms: - source_paths: - - language.protected_terms + origin: genre_native terms: - nicht - kein @@ -1683,15 +1609,7 @@ parameters: meta_term_groups: - presentation retrieval_vocabulary_views: - source_paths: - - vocabulary.views.retrieval.generic_product_tokens.add - - vocabulary.views.retrieval.important_short_model_tokens.add - - vocabulary.views.retrieval.family_descriptor_tokens.add - - vocabulary.views.retrieval.looks_like_reagent_tokens.add - - vocabulary.views.retrieval.looks_like_safety_docs.add - - vocabulary.views.retrieval.looks_like_device_words.add - - vocabulary.views.retrieval.looks_like_document_words.add - - vocabulary.views.retrieval.looks_like_safety_words.add + origin: genre_native generic_product_tokens: - produkt - produkte @@ -1818,15 +1736,7 @@ parameters: - lagerung - piktogramm exact_selection: - source_paths: - - retrieval.exact_selection_token_variant_prefixes - - retrieval.exact_selection_indicator_question_tokens - - retrieval.exact_selection_indicator_question_phrases - - retrieval.exact_selection_indicator_table_heading_patterns - - retrieval.exact_selection_indicator_table_header_patterns - - retrieval.exact_selection_indicator_table_row_patterns - - retrieval.exact_selection_indicator_table_required_primary_terms - - retrieval.exact_selection_indicator_table_required_context_terms + origin: genre_native token_variant_prefixes: indikator: - indikator @@ -1889,8 +1799,7 @@ parameters: governance_and_regression: description: Current guardrail terms that intentionally protect this genre during audits and regression checks. regression_baseline: - source_paths: - - governance.regression_baseline + origin: genre_native protected_short_model_tokens: - th - tc @@ -1947,8 +1856,7 @@ parameters: - ph - redox vocabulary_guardrails: - source_paths: - - governance.vocabulary + origin: genre_native core_pattern_audit: source_paths: - governance.core_pattern_audit diff --git a/patch_history/RETRIEX_PATCH_59A_GENRE_NATIVE_SOURCEPATH_CLEANUP_README.md b/patch_history/RETRIEX_PATCH_59A_GENRE_NATIVE_SOURCEPATH_CLEANUP_README.md new file mode 100644 index 0000000..482fadd --- /dev/null +++ b/patch_history/RETRIEX_PATCH_59A_GENRE_NATIVE_SOURCEPATH_CLEANUP_README.md @@ -0,0 +1,109 @@ +# RetrieX Patch 59A - Genre Native Source Path Cleanup + +## Ziel + +Dieser Patch startet die kontrollierte Aufraeumphase nach der Genre-Zentralisierung. +Er entfernt leere Legacy-`source_paths` aus `config/retriex/genre.yaml`, ohne eingefrorene +nicht-leere Fallbacks, Runtime-Resolved-Pfade oder fachliche Runtime-Logik zu veraendern. + +## Hintergrund + +Nach p58 ist `genre.yaml` fuer viele fachliche Werte bereits die zentrale Pflegeflaeche. +Die alten YAML-Pfade wurden aber noch als leere Legacy-Fallbacks in +`genre.configuration_values.*.source_paths` mitgefuehrt. Diese Referenzen sind fuer die +fachliche Pflege nicht mehr notwendig und erzeugen unnoetige Audit-/Migrationslast. + +## Aenderungen + +### `config/retriex/genre.yaml` + +- 92 leere Legacy-`source_paths` wurden entfernt. +- 30 Value-Nodes, die dadurch keine Legacy-Quelle mehr brauchen, wurden mit + `origin: genre_native` markiert. +- Gemischte Nodes behalten weiterhin ihre nicht-leeren/frozen Source-Pfade. +- Nicht-leere frozen Fallbacks bleiben unveraendert. +- Runtime-Resolved-Pfade bleiben unveraendert: + - `commerce.max_shop_results` + - `commerce.store_api_base_url` + +### `src/Config/GenreSourceOfTruthGuard.php` + +- Der Guard erlaubt nun `origin: genre_native` als explizite native Herkunft fuer + `genre.configuration_values`-Nodes. +- `origin` wird als Metadatenfeld behandelt, analog zu `source_paths` und `description`. +- Native Nodes zaehlen als abgedeckt und muessen keine Legacy-`source_paths` mehr deklarieren. +- Die Guard-Summary enthaelt zusaetzlich `genre_native_value_nodes`. + +## Bewusst nicht geaendert + +- Keine Aenderung an Retrieval-, Prompt-, Scoring-, Commerce- oder Shopware-Runtime-Logik. +- Keine neuen fachlichen Token-/Stringlisten im PHP-Core. +- Keine Entfernung der eingefrorenen nicht-leeren Fallbacks. +- Keine Entfernung technischer Runtime-Pfade. +- Keine Bereinigung der `adaptation_surface`-Inventarlisten; diese sollten separat klassifiziert werden. + +## Ergebnis der lokalen Strukturpruefung + +Vor Patch: + +- 131 deklarierte `source_paths` in `genre.configuration_values` +- 92 davon zeigten auf leere Legacy-Fallbacks +- 39 zeigten auf nicht-leere/frozen bzw. Runtime-resolved Werte + +Nach Patch: + +- 39 deklarierte `source_paths` +- 0 leere Legacy-`source_paths` +- 30 `genre_native` Value-Nodes +- 0 Coverage-Fehler in der lokalen Guard-Simulation +- 0 fehlende frozen Source-Pfade +- 0 frozen Hash-Mismatches in der lokalen Guard-Simulation + +## Lokale Checks + +Ausgefuehrt im entpackten Patch-Arbeitsstand: + +```bash +php -l src/Config/GenreSourceOfTruthGuard.php +php -l src/Config/GenreConfig.php +php -l src/Config/RetriexEffectiveConfigProvider.php +``` + +Alle PHP-Lints waren erfolgreich. + +Zusaetzlich wurde `config/retriex/genre.yaml` mit PyYAML geparst und eine lokale +Guard-Simulation fuer Source-Path-Abdeckung, leere Legacy-Pfade und frozen Hashes ausgefuehrt. + +## Nicht lokal ausfuehrbar + +Die Symfony-Console-Checks konnten in der bereitgestellten ZIP nicht ausgefuehrt werden, +da `vendor/` fehlt: + +```text +Dependencies are missing. Try running "composer install". +``` + +Bitte im vollstaendigen Projekt mit Dependencies ausfuehren: + +```bash +bin/console mto:agent:config:validate +bin/console mto:agent:regression:test +bin/console mto:agent:config:audit-source --details +bin/console mto:agent:config:audit-patterns --details +``` + +## Erwartete Audit-Auswirkung + +`genre_value_paths_with_source_paths` und `genre_declared_source_paths` sollten sinken, +weil leere Legacy-Referenzen entfernt wurden. Das ist beabsichtigt. + +`legacy_fallback_empty` sollte im Genre-Source-of-Truth-Guard auf 0 fallen. +`genre_native_value_nodes` sollte die nativ markierten Value-Nodes ausweisen. + +## Naechster moeglicher Patch + +p59B sollte die `adaptation_surface`-Inventarlisten separat klassifizieren: + +- Welche Pfade sind weiterhin technische Review-/Audit-Inventare? +- Welche leeren Legacy-Pfade koennen auch aus der Surface-Liste entfernt werden? +- Welche nicht-leeren frozen Pfade bleiben bewusst technische Fallbacks? diff --git a/public/assets/styles/base.css b/public/assets/styles/base.css index deedc4f..a28382f 100644 --- a/public/assets/styles/base.css +++ b/public/assets/styles/base.css @@ -58,7 +58,7 @@ input, textarea, select { } .container { - max-width: 900px; + max-width: 1024px; margin: 0 auto; display: flex; flex-direction: column; @@ -85,8 +85,8 @@ input, textarea, select { flex: 1; overflow-y: auto; padding: 1rem; - background: #121a25; - /*border: 1px solid var(--border);*/ + /* background: #121a25; + border: 1px solid var(--border);*/ border-radius: 6px 6px 0 0; /*box-shadow: 0px 0px 20px #ffffff26;*/ } diff --git a/src/Config/GenreSourceOfTruthGuard.php b/src/Config/GenreSourceOfTruthGuard.php index 5029fb2..0165ed4 100644 --- a/src/Config/GenreSourceOfTruthGuard.php +++ b/src/Config/GenreSourceOfTruthGuard.php @@ -96,6 +96,7 @@ final readonly class GenreSourceOfTruthGuard $coverageErrors = $this->validateConfigurationValueCoverage($configurationValues); array_push($errors, ...$coverageErrors); + $nativeValueNodes = $this->countGenreNativeValueNodes($configurationValues); $declaredSourcePaths = $this->collectSourcePaths($configurationValues); $uniqueSourcePaths = []; foreach ($declaredSourcePaths as $valuePath => $sourcePaths) { @@ -157,6 +158,7 @@ final readonly class GenreSourceOfTruthGuard $summary = $this->summarizeRows($rows); $summary['configuration_value_groups'] = count($configurationValues); + $summary['genre_native_value_nodes'] = $nativeValueNodes; $summary['source_path_value_nodes'] = count($declaredSourcePaths); $summary['declared_source_paths'] = count($uniqueSourcePaths); $summary['violations'] = count($errors); @@ -190,10 +192,15 @@ final readonly class GenreSourceOfTruthGuard { $sourcePaths = $value['source_paths'] ?? null; $hasSourcePaths = is_array($sourcePaths) && $sourcePaths !== []; + $hasGenreNativeOrigin = $this->hasGenreNativeOrigin($value); if (array_key_exists('source_paths', $value) && !$hasSourcePaths && $path !== '') { $errors[] = sprintf('genre.configuration_values.%s.source_paths must be a non-empty list when declared.', $path); } + if (array_key_exists('origin', $value) && !$hasGenreNativeOrigin && $path !== '') { + $errors[] = sprintf('genre.configuration_values.%s.origin must be genre_native when declared.', $path); + } + if ($hasSourcePaths) { $seen = []; foreach ($sourcePaths as $sourcePath) { @@ -209,13 +216,13 @@ final readonly class GenreSourceOfTruthGuard } } - $covered = $coveredBySourcePath || $hasSourcePaths; + $covered = $coveredBySourcePath || $hasSourcePaths || $hasGenreNativeOrigin; if ($path !== '' && !$covered && $this->hasDirectPayload($value)) { $errors[] = sprintf('genre.configuration_values.%s must declare source_paths or inherit them from a parent value node.', $path); } foreach ($value as $key => $child) { - if ($key === 'source_paths' || $key === 'description' || !is_string($key) || !is_array($child)) { + if ($this->isMetadataKey($key) || !is_string($key) || !is_array($child)) { continue; } @@ -230,7 +237,7 @@ final readonly class GenreSourceOfTruthGuard private function hasDirectPayload(array $value): bool { foreach ($value as $key => $child) { - if ($key === 'source_paths' || $key === 'description') { + if ($this->isMetadataKey($key)) { continue; } if (!is_array($child)) { @@ -241,6 +248,45 @@ final readonly class GenreSourceOfTruthGuard return false; } + + /** @param array $value */ + private function hasGenreNativeOrigin(array $value): bool + { + $origin = $value['origin'] ?? null; + + return is_string($origin) && trim($origin) === 'genre_native'; + } + + private function isMetadataKey(mixed $key): bool + { + return $key === 'source_paths' || $key === 'description' || $key === 'origin'; + } + + /** + * @param array $configurationValues + */ + private function countGenreNativeValueNodes(array $configurationValues): int + { + return $this->countGenreNativeValueNodesRecursive($configurationValues, ''); + } + + /** @param array $value */ + private function countGenreNativeValueNodesRecursive(array $value, string $path): int + { + $count = $path !== '' && $this->hasGenreNativeOrigin($value) ? 1 : 0; + + foreach ($value as $key => $child) { + if ($this->isMetadataKey($key) || !is_string($key) || !is_array($child)) { + continue; + } + + $childPath = $path === '' ? $key : $path . '.' . $key; + $count += $this->countGenreNativeValueNodesRecursive($child, $childPath); + } + + return $count; + } + /** * @param array $configurationValues * @return array @@ -277,7 +323,7 @@ final readonly class GenreSourceOfTruthGuard } foreach ($value as $key => $child) { - if ($key === 'source_paths' || $key === 'description' || !is_string($key) || !is_array($child)) { + if ($this->isMetadataKey($key) || !is_string($key) || !is_array($child)) { continue; } $childPath = $path === '' ? $key : $path . '.' . $key; @@ -485,6 +531,7 @@ final readonly class GenreSourceOfTruthGuard 'configuration_value_groups' => 0, 'source_path_value_nodes' => 0, 'declared_source_paths' => 0, + 'genre_native_value_nodes' => 0, 'legacy_fallback_empty' => 0, 'legacy_frozen_non_empty' => 0, 'legacy_non_empty_unregistered' => 0,