Row final builder modularization¶
includes/mapping/common/row-final-builder.php is the historical final row assembly stage used by ccx_feeds_row_to_item_build_final().
The refactor keeps that public function and its ordering semantics while moving coherent responsibilities into includes/mapping/common/row-final/ modules.
Documentation discipline¶
This document is updated as part of every structural migration step, not after the refactor is finished. A step is only considered structurally complete when code, runtime contracts, and this architecture document describe the same state.
For each extracted boundary, this document records:
- the module path and responsibility;
- whether the module is only prepared/validated or actually wired into runtime;
- the effective runtime order around the boundary;
- compatibility behavior and historical exceptions that must remain stable;
- the Runtime contract protecting the migration.
Prepared modules must not be documented as active runtime delegation until their call site has actually been replaced and validated.
Current structure¶
Current modules:
05-xcover-preflight.php10-media-scope.php15-platform-fallback.php20-normalization.php25-technical-model.php27-identity-reload.php30-stock.php35-industrial-tech.php40-home-maintenance.php45-generic-appliances.phprow-final-builder.phpfor orchestration, canonical row assembly, and final return
The builder currently loads and delegates to all ten extracted modules from 05-xcover-preflight.php through 45-generic-appliances.php.
Current runtime order¶
The effective high-level order is:
media/scope -> delegated XCover identity/classifier preflight -> canonical row assembly (delegated platform fallback) -> normalization -> technical model fallback -> normalized identity reload -> stock -> industrial tech corrections -> home/maintenance corrections -> generic appliance corrections -> return
This order is intentionally protected because later stages depend on normalized fields created by earlier ones.
Extracted boundaries¶
05-xcover-preflight.php¶
Status: wired and validated.
Owns the historical CCX_XCOVER_FINAL_IDENTITY_V1 pre-build rule:
- match
Galaxy XCover <number>with optionalProusing the historical regular expression; - force
brand_normtoSamsung; - normalize the matched XCover model into
model_norm; - force
classifier_vertical_id,classifier_spec_type_probable, andclassifier_spec_type_finaltosmartphonebefore canonical row construction.
The preflight is intentionally distinct from the later CCX_SmartphoneNormalizer XCover rule. The preflight mutates the identity/classifier seed before $row is constructed; the smartphone normalizer runs later inside 20-normalization.php, after category routing and offer-key construction, and additionally manages runtime normalization metadata such as confidence/reason/accessory cleanup. Treating the two rules as interchangeable would therefore change execution timing and potentially behavior.
row-final-builder.php now requires 05-xcover-preflight.php, delegates through ccx_feeds_row_final_xcover_preflight($title_raw, $brand_norm, $model_norm, $args), reloads brand_norm and model_norm, and guardedly reloads $args before canonical row construction. The historical inline CCX_XCOVER_FINAL_IDENTITY_V1 block has been removed.
tests/Runtime/RowFinalXCoverPreflightBoundaryContractTest.php locks the extracted mutation surface and required order XCover preflight -> canonical row -> normalization. tests/Runtime/RowFinalBuilderBoundaryContractTest.php also locks the delegated require/call/reload sequence and rejects regression to the old inline marker.
10-media-scope.php¶
Status: wired and validated.
Owns:
- final image URL sanitization;
- shared final-row scalar sanitization;
- Effinity-aware out-of-scope decision;
- alternate-image sanitization;
- gallery de-duplication;
image_gallery_jsongeneration.
row-final-builder.php delegates this stage through ccx_feeds_row_final_media_scope($args) and no longer contains the historical inline media/scope implementation.
15-platform-fallback.php¶
Status: wired and validated.
Owns the exact historical fallback used when identity does not already provide platform_norm, including:
- Switch 2 / Switch;
- Amiibo;
- Nintendo 3DS / DS;
- Wii U / Wii;
- retro platforms;
- PlayStation generations, PlayStation Store and generic PlayStation;
- Xbox Series / One / generic Xbox;
- PC;
unknown_game_platformfor the historical video-game category fallback.
row-final-builder.php now delegates this field through ccx_feeds_row_final_platform_norm($identity, $title_raw, $category_path_raw, $args) during canonical row construction. The historical inline closure has been removed from the builder.
The repository already has common/platform.php, but that helper is not behaviorally identical to the final-row fallback. It covers the main console families and adds its own normalization/false-positive rules, while the historical builder additionally recognizes Amiibo, Nintendo handheld/Wii families, PlayStation Store/generic PlayStation and the category-level unknown fallback. Reusing ccx_mapping_detect_console_platform() directly would therefore change current matching semantics. The extracted module intentionally preserves the builder behavior first; consolidation with common/platform.php can be considered separately only with dedicated equivalence tests.
20-normalization.php¶
Status: wired and validated.
Owns, in historical order:
ccx_feeds_apply_category_vertical_routing();ccx_feeds_build_offer_key_v1();CCX_SmartphoneNormalizer::normalize();CCX_VerticalAttributesNormalizer::normalize().
row-final-builder.php delegates this stage through ccx_feeds_row_final_normalize($row) before the generic technical model fallback.
25-technical-model.php¶
Status: wired and validated.
Owns the conservative generic technical-model fallback used after normalization when:
model_normis empty;brand_normis present;- the resolved vertical is one of
monitor,projector,tablet, orpc_storage.
The module preserves the historical candidate extraction and scoring rules, including rejection of display/interface/unit labels, the short-family penalty, dash preference, and highest-score selection via array_key_first() after descending sort.
row-final-builder.php now delegates this stage through ccx_feeds_row_final_technical_model($row) immediately after normalization and before normalized identity reload. The historical inline technical-model block has been removed from the builder.
27-identity-reload.php¶
Status: wired and validated.
Owns the normalized-identity reload performed after the technical-model fallback and before stock normalization:
- sanitize
brand_norm,model_norm, andstorage_normfrom the current row, falling back to the previously seeded local values; - normalize empty strings back to
nullon the row; - return the updated row without changing any other field.
row-final-builder.php now delegates this stage through ccx_feeds_row_final_identity_reload($row, $brand_norm, $model_norm, $storage_norm). The historical inline CCX_FINAL_NORMALIZED_IDENTITY_RELOAD_V1 block has been removed. Its runtime position remains technical model -> normalized identity reload -> stock.
30-stock.php¶
Status: wired and validated.
Owns the characterized final stock normalization:
stock_status/in_stockraw acquisition;- truthy/falsy stock aliases;
- quantity
<= 0handling; - final
in_stockandstock_statusfields.
row-final-builder.php delegates this stage through ccx_feeds_row_final_stock($row, $args) immediately after normalized identity reload and before the historical vertical correction families. The historical inline stock block has been removed from the builder.
35-industrial-tech.php¶
Status: wired and validated.
Owns the first small post-stock correction family:
- detect Note PC / Galaxy Book / notebook / laptop signals and force the
laptopvertical; - detect projector / projecteur / vidéoprojecteur signals and force the
projectorvertical; - preserve the historical classifier confidence floor of
92; - preserve the historical reasons
industrial_laptop_note_pcandindustrial_projector; - clear
review_requiredfor these confident corrections.
row-final-builder.php now delegates this stage through ccx_feeds_row_final_industrial_tech($row) immediately after stock and before the home/maintenance rules. The historical inline Samsung Note PC / projector block has been removed from the builder.
40-home-maintenance.php¶
Status: wired and validated.
Owns the full historical home/maintenance if / elseif chain after industrial-tech corrections. The chain intentionally remains together in one module because its first-match-wins precedence is part of current behavior.
The module preserves, in order:
steam_iron/home_steam_iron_rule;garment_steamer/home_garment_steamer_rule;air_treatment/home_air_treatment_rule;fan/home_fan_rule;heater/home_heater_rule;home_cleaning/home_cleaning_rule;pet_appliance/home_pet_appliance_rule;laundry_accessory/home_laundry_accessory_rule;outdoor_garden/home_outdoor_garden_rule.
All nine branches retain the historical classifier confidence floor of 92 and clear review_required. row-final-builder.php delegates this stage through ccx_feeds_row_final_home_maintenance($row) immediately after ccx_feeds_row_final_industrial_tech($row) and before the generic appliance block. The historical inline home/maintenance chain has been removed from the builder.
45-generic-appliances.php¶
Status: wired and validated.
Owns the final historical appliance-classification if / elseif chain. The chain remains one boundary because its first-match-wins precedence is part of current behavior.
It preserves, in order:
dryer/industrial_appliance_dryer;dishwasher/industrial_appliance_dishwasher;washing_machine/industrial_appliance_washing_machine;cooktop/industrial_kitchen_cooktop.
The helper preserves the historical classifier confidence floor of 90, sets vertical_id, spec_type_probable, and spec_type_final together, stores the historical reason, and clears review_required.
row-final-builder.php now delegates this stage through ccx_feeds_row_final_generic_appliances($row) immediately after ccx_feeds_row_final_home_maintenance($row) and immediately before return $row;. The historical inline generic-appliance chain has been removed from the builder.
The protected tail order is now home/maintenance -> generic appliances -> return.
Responsibilities still in row-final-builder.php¶
The builder now primarily owns orchestration and the remaining final-row assembly responsibilities:
- normalized identity seed extraction;
- construction of the canonical row payload, excluding delegated XCover preflight and platform fallback;
- ordered delegation through the extracted normalization/correction stages;
- final return.
Further extraction should only happen where it produces a genuinely clearer responsibility boundary without obscuring the orchestration contract.
Compatibility and contracts¶
The public function remains:
ccx_feeds_row_to_item_build_final(array $args): ?array
tests/Runtime/RowFinalBuilderBoundaryContractTest.php locks the historical entrypoint and the ordering/delegation boundaries through the full post-stock correction tail, including the delegated XCover preflight and generic appliances -> return.
The dedicated Runtime contracts for XCover preflight, technical model, identity reload, industrial tech, home maintenance, and generic appliances lock their respective module surfaces and runtime order.
tests/Runtime/RowFinalXCoverPreflightBoundaryContractTest.php confirms runtime delegation through ccx_feeds_row_final_xcover_preflight(...), preserves the historical XCover mutation surface, rejects regression to the inline implementation, and protects XCover preflight -> canonical row -> normalization.
tests/Runtime/RowFinalGenericAppliancesBoundaryContractTest.php confirms runtime delegation through ccx_feeds_row_final_generic_appliances($row), rejects drift back to the historical inline implementation, preserves the four historical reasons and first-match order inside the module, and protects the home/maintenance -> generic appliances -> return boundary.
Latest validated checkpoint after wiring 05-xcover-preflight.php:
- dedicated
RowFinalXCoverPreflightBoundaryContractTest: 2 tests, 23 assertions; - dedicated
RowFinalBuilderBoundaryContractTest: 6 tests, 174 assertions; - full PHPUnit suite: 771 tests, 5228 assertions;
- 3 PHPUnit warnings and no failures;
- repository audit: 270 PASS, 0 INFO, 0 WARN, 0 FAIL.
Every structural step is validated with PHP lint, the dedicated contract, the full PHPUnit suite, and the repository audit before integration. The documentation is updated in the same migration step so it remains a reliable map of the actual codebase rather than a retrospective summary.