This commit is contained in:
team 1
2026-05-07 14:42:12 +02:00
parent 5237bb0c63
commit f136bec0d7
4 changed files with 193 additions and 129 deletions

View File

@@ -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<value>\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<value>\d+(?:[,.]\d+)?)\s*(?P<unit>°?\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<value>\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

View File

@@ -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?

View File

@@ -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;*/
}

View File

@@ -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<int|string, mixed> $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<string, mixed> $configurationValues
*/
private function countGenreNativeValueNodes(array $configurationValues): int
{
return $this->countGenreNativeValueNodesRecursive($configurationValues, '');
}
/** @param array<int|string, mixed> $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<string, mixed> $configurationValues
* @return array<string, string[]>
@@ -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,