Aller au contenu

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:

  1. 05-xcover-preflight.php
  2. 10-media-scope.php
  3. 15-platform-fallback.php
  4. 20-normalization.php
  5. 25-technical-model.php
  6. 27-identity-reload.php
  7. 30-stock.php
  8. 35-industrial-tech.php
  9. 40-home-maintenance.php
  10. 45-generic-appliances.php
  11. row-final-builder.php for 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 optional Pro using the historical regular expression;
  • force brand_norm to Samsung;
  • normalize the matched XCover model into model_norm;
  • force classifier_vertical_id, classifier_spec_type_probable, and classifier_spec_type_final to smartphone before 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_json generation.

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_platform for 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:

  1. ccx_feeds_apply_category_vertical_routing();
  2. ccx_feeds_build_offer_key_v1();
  3. CCX_SmartphoneNormalizer::normalize();
  4. 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_norm is empty;
  • brand_norm is present;
  • the resolved vertical is one of monitor, projector, tablet, or pc_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, and storage_norm from the current row, falling back to the previously seeded local values;
  • normalize empty strings back to null on 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_stock raw acquisition;
  • truthy/falsy stock aliases;
  • quantity <= 0 handling;
  • final in_stock and stock_status fields.

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 laptop vertical;
  • detect projector / projecteur / vidéoprojecteur signals and force the projector vertical;
  • preserve the historical classifier confidence floor of 92;
  • preserve the historical reasons industrial_laptop_note_pc and industrial_projector;
  • clear review_required for 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:

  1. steam_iron / home_steam_iron_rule;
  2. garment_steamer / home_garment_steamer_rule;
  3. air_treatment / home_air_treatment_rule;
  4. fan / home_fan_rule;
  5. heater / home_heater_rule;
  6. home_cleaning / home_cleaning_rule;
  7. pet_appliance / home_pet_appliance_rule;
  8. laundry_accessory / home_laundry_accessory_rule;
  9. 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:

  1. dryer / industrial_appliance_dryer;
  2. dishwasher / industrial_appliance_dishwasher;
  3. washing_machine / industrial_appliance_washing_machine;
  4. 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.