Skip to content

File Lineage — Mirakl / PIM / Offers ​

Companion to data-lineage.md (which traces domains — description, ID matching, stock — with prose-first citations). This document instead catalogs every file each task reads and writes, C4-context style: ecom_products_all (jobs-alg-all.py) and ecom_w1_full (jobs-inf-w1-full.py) as the two DAGs, one file-flow table each, plus a consolidated cross-DAG handoff table. Drill into a specific field's construction only where data-lineage.md already does so (§1–§6).

Grounded in direct reads of jobs-alg-all.py, jobs-inf-w1-full.py, bloomreach/xmlbuilder/all_products.py, bloomreach/xmlbuilder/supply_sync.py, and jobs-shw-gm.py in /Users/pmccluskey/work/pim/airflow-pim-dags. jobs-inf-w1-full-pre.py/jobs-inf-w1-delta.py/jobs-inf-w1-delta-pre.py follow the same file-flow skeleton as jobs-inf-w1-full.py — see "Diffs" below for where they genuinely differ.

Scope note ​

This covers ecom_products_all and ecom_w1_full (representative of the four W1 DAGs) only — the same two-DAG slice data-lineage.md documents. jobs-shw-gm.py (a per-BU Shopware DAG) appears only as a confirmed consumer of W1's output, not fully catalogued itself. Per-BU Algolia DAGs (jobs-alg-{gm,ec,pp,bk}-inr.py) were checked only for the specific {bu}_product.json question below, not catalogued in full.

ecom_products_all (jobs-alg-all.py) ​

Task IDpython_callableInput file(s)Output file(s)Notes
Prepare-Environment(bash)—temp/import/intermediate/staging dirs (cleared)
Download-PIM(bash)S3 ALL_S3_PIM2_DIRECTORYimport/all.zip
Unpack-PIM(bash)import/all.zipunpacked PIM CSVs in import/
Download-Schema(bash)S3 schema/{target}/intermediate/{schema_template_file}
Download-BK-ERP-Datas3_download_by_stsS3 BK_S3_ERP_SRC (STS role)import/AVAILABILITY_BK.CSV
Download-FC-ERP-Datas3_download_by_stsS3 BK_S3_ERP_SRC (STS role)import/AVAILABILITY_FC.CSV
Download-BK-Regions-Map(bash)S3 BK_S3_SRC/erp-dataimport/daily_branch_data_bk.csv
Normalize-BK-Regions-MapbranchNormalizer.normalize_mincronimport/daily_branch_data_bk.csvintermediate/branch_data_BK.csvalso caches to S3
Download-CE-ERP-Datas3_downloadS3 CE_S3_SRC/erp-dataimport/AVAILABILITY_CE.csv
Download-EC-ERP-Datas3_download_by_stsS3 EC_S3_ERP_SRC (STS role)import/AVAILABILITY_EC_NEW.csv
Get-GM/ACME/HandC-Regions-Mapjane.get_regions_{gm,ac,hc}Jane/Conduit APIimport/{GM,AC,HC}_branches.csvHTTP pull, not file transform
Download-GM-ERP-Datas3_downloadS3 GM_S3_MINCRON_SRC/mincronimport/mincron.zip
Unpack-GM-ERP-Data(bash)import/mincron.zipimport/AlgoliaExport.json
Split-GM-ERP-Data(bash)import/AlgoliaExport.jsonproduct_family_export.json, product_headers.json, product_location_info.json
Convert-GM-AvailabilityavailabilityNormalizer.convert_mincron_jsonimport/product_headers.jsonimport/erp_pim_data_GM.csv, AVAILABILITY_GM.csv
Split-GM-AvailabilityavailabilityNormalizer.split_gm_availabilityAVAILABILITY_GM.csv + branch mapsAVAILABILITY_AC.csv, AVAILABILITY_HC.csv
Download-PP-ERP-Datas3_downloadS3 PP_AVAILABILITY_S3_SRCimport/AVAILABILITY_PP.csv
Convert-CE-AvailabilityavailabilityNormalizer.convert_ce_dataAVAILABILITY_CE.csvsame file, normalized
Download-TE-ERP-Data → Convert-TE-AvailabilityavailabilityNormalizer.convert_te_dataimport/TECExport.csv → AVAILABILITY_TE.csverp_pim_data_TE.csv
Download/Convert-Neuco-*availabilityNormalizer.convert_neuco_dataAVAILABILITY_NEUCO.csv, NEUCO-items.csvattr_data_NEUCO.csv, erp_pim_data_NC.csvgated by Airflow Variable
Download-Rheem-Data → Convert-Rheem-AvailabilityavailabilityNormalizer.convert_rheem_dataRheem FTP CSV + endless-aisle fileattr_data_RHEEM.csv, erp_pim_data_RM.csvgated by Airflow Variable
Download-{BK,CE,EC,GM,HM,PP}-PIM-Data(bash, optional)S3 {BU}_S3_SRC/erp-data/{target}erp_pim_data_{BU}.csv, catalog_code_data_{BU}.csv, uom_data_{BU}.csv
File-Cleanup → Re-Encode(bash)import/*intermediate/* (UTF-8)import → intermediate boundary
Handle-Attributesall_etl.merge_attrsintermediate/attributes_data.csvprocessed attributes
Handle-Structuresall_etl.process_structureintermediate/structures_data.csvprocessed structures
Merge-Baker / Split-Homans / Drop-Obsolete-ColumnsavailabilityNormalizer.{merge_baker,split_homans,normalize_cols}per-BU availability CSVsnormalized per-BU availability CSVs
Merge-Norm-AvailabilityavailabilityNormalizer.mergeall intermediate/AVAILABILITY_*.csvintermediate/availability.csvNC/RM inclusion gated by Variables
Merge-ERP-Dataall_etl.merge_erperp_pim_data_{BU}.csv × all BUs, items fileintermediate/erp_data.csv
Merge-Catalog-Codes / Merge-UOM-Dataall_etl.merge_erpcatalog_code_data_{BU}.csv / uom_data_{BU}.csvcatalog_code_data.csv / uom_data.csv
Translateall_etl.processall merged intermediate CSVs + availability.csv + erp_data.csvstaging/all_products.jsonmain transform — builds the availability field via _availability_dict(), see data-lineage.md §3
Count-Check / Schema-Checkcount_utils.simple_json_count / json_utils.schema_checkstaging/all_products.jsonpass/fail signalglob correct in this file (no backtick bug)
Transferjson_utils.push_index_simplestaging/all_products.jsonAlgolia index (ALL_ALGOLIA_INDEX)
Upload-W1(bash)staging/all_products.jsonS3 W1_S3_ARCHIVE/w1_json_feeds/{target}/all_products.jsoncross-DAG handoff point — see table below
Archive(bash)staging/all_products.json, import dirS3 ALL_S3_ARCHIVE

ecom_w1_full (jobs-inf-w1-full.py) ​

Task IDpython_callableInput file(s)Output file(s)Notes
Prepare-Environment(bash)—temp/import/intermediate/staging dirs (cleared)
Download-Sales → Unpack → Convert(bash)S3 SS_S3_SRC/pendingimport/ss_sales_qtyshipped.csvASCII-normalized
Download-ALL-PIM / Download-W1-PIM(bash)S3 ALL_S3_PIM2_DIRECTORY / W1_S3_PIM2_DIRECTORYunpacked PIM/W1 CSVs in import/
Download-Schema(bash, optional)S3 W1/schema/{target}intermediate/{schema_template_file}, {supply_sync_schema_template_file}
Check-CSV-Headers / Check-Sales-Hashbash_utils.validate_header / file_utils.hashes_checksales file vs headers/hash cachepass/fail signal, SNS alert on failure
Merge-Full-PID(bash)per-BU dirs under intermediate/full_pid.csv
Download-BU-Feeds(bash)S3 W1_S3_ARCHIVE/w1_json_feeds/{target}/*_product*.jsonintermediate/*_product*.jsoncross-DAG handoff — this glob downloads all_products.json itself; see correction below
File-Cleanup → Re-Encode(bash)import/*intermediate/* (UTF-8)
Get-PIM-List(bash)intermediate/itemdataflex.csvpim_list.csv
Handle-Attributes / Structures / Categories / Item-Flexall_etl.{merge_attrs,process_structure,process_category,process_flex}respective intermediate/*_data.csvprocessed attribute/structure/category/flex datasee data-lineage.md §1
Merge-ERP-Data / Catalog-Codes / Descriptions / Status / Title / UOM / URL-Keyall_etl.merge_bu_erp (7 calls, one per field)per-BU field data, items fileerp_data.csv, catalog_code_data.csv, description_data.csv, status_data.csv, title_data.csv, uom_data.csv, url_keys_data.csvsee data-lineage.md §1/§2
Merge-BU-Attrsall_etl.merge_bu_attrsper-BU attr exports, items filebu_attr_data.csvfeeds Shopware + Supply-Sync steps
Translatemirakl_etl.processall merged intermediate CSVsstaging/w1_products.jsonno availability field — see data-lineage.md §3
Count-Check / Schema-Checkcount_utils.simple_json_count / json_utils.schema_checkstaging/w1_products.jsonpass/fail signalBUG: Schema-Check's glob is "w1_products.\json"(stray backtick) — matches nothing; isolated to this file,delta.py/delta-pre.py` use the correct pattern
Upload-W1-Feed(bash)staging/w1_products.jsonS3 W1_S3_ARCHIVE/W1/{target}.w1_products*.jsoncross-DAG handoff — consumed by jobs-shw-gm.py's Download-W1-Feed
Shopware-Products / BU-Data / Association / Media / Dimensions / Descriptions / Titles / Taxonomy / Attributionshopware_etl.shopware_*_w1w1_products.json, items.csv, bu_attr_data.csv, mirakl_pid_2.csv, category/attribute filestmp/shopware_*.csv (10 files)
Pack-Shopware / Upload-Shopware(bash)10 tmp/shopware_*.csvtmp/shopware.tar.gz → S3 SHOPWARE_S3_ARCHIVE/shopware_import_files
Supply-Sync-Algoliasupply_sync_etl.supply_sync_algolia_processitems.csv, all_products.json (the file Download-BU-Feeds just downloaded — confirmed same file, see correction below), bu_attr_data.csv, mirakl_pid_2.csv, shopware_descriptions.csv, shopware_titles.csv, shopware_item_taxonomy.csv, taxonomy_paths.csv, mirakl_offers.csvtmp/supply_sync.jsonpass-through of all_products.json's availability field — see data-lineage.md §3
Supply-Sync-Schema-Check / Transfer / Upload-Supply-Syncjson_utils.{schema_check,push_index_simple} / bashtmp/supply_sync.jsonAlgolia index (SUPPLY_SYNC_ALGOLIA_INDEX); S3 SHOPWARE_S3_ARCHIVE/supply_sync_feed
Full-Add-Products / Mirakl-Get-Products / Mirakl-Split-Productsmirakl_etl.{filter_products,get_products,split_products}full_pid.csv, w1_products.json, Mirakl APIinsert_pid.csv, update_pid.csv, delete_pid.csv
Mirakl-Insert/Update/Delete-Productsmirakl_etl.mirakl_products_processw1_products.json + respective pid listmirakl_{insert,update,delete}_products.json
Mirakl-Get-Taxonomy / Mirakl-Taxonomy / Push / Report / Errorsmirakl_etl.{get_taxonomy,mirakl_taxonomy_process,push_taxonomy,report_taxonomy,err_taxonomy}category.csv, Mirakl APImirakl_taxonomy.csv, taxonomy_paths.csv (feeds Supply-Sync-Algolia)Push gated by MIRAKL_PUSH_DISABLED
Mirakl-Taxonomy-Delete-Push(dummy)——real logic commented out — see anomaly below
Mirakl-Get-Offers / Aggregate-Offersmirakl_etl.{get_offers,aggr_offers}Mirakl APImirakl_offers.csv (feeds Supply-Sync-Algolia)
Mirakl-Operator-Insert/Update-Push / Report / Errorsmirakl_etl.{push_operator_products,report_operator_products,err_operator_products}mirakl_{insert,update}_products.jsontracking IDs, error CSVsPush gated by MIRAKL_PUSH_DISABLED
Mirakl-Operator-Delete-Push(dummy)——real logic commented out — see anomaly below
Mirakl-Get-Products-2mirakl_etl.get_productsMirakl APImirakl_pid_2.csvre-fetch feeding all Shopware-* and Supply-Sync tasks
Mirakl-Get-Deleted-Productsmirakl_etl.get_deleted_productsMirakl APImirakl_deleted_products.json
Handle-Uncategorized / Inactive / Inactive-CEUSmirakl_etl.handle_{uncategorized,inactive,inactive_ceus}w1_products.jsonreport CSVs → S3 W1/reports/full
Pack-Report / Report-Notifybash / upload_and_messagetmp report filesreport.tar.gz; SNS notification; S3 W1/reports/full
Archive(bash)w1_products.json, supply_sync.json, import dirS3 W1_S3_ARCHIVE

Diffs: full-pre / delta / delta-pre vs. full ​

  • full-pre: same task IDs and file-flow skeleton; differs only in environment wiring (FULL_PRE base path, PREPROD Mirakl/Algolia endpoints, W1_PRE/supply_sync_feed_pre S3 paths). Mirakl-Taxonomy-Delete-Push/Mirakl-Operator-Delete-Push are live PythonOperators here (not dummies) — a functional divergence from full, not just naming.
  • delta: structural differences — adds a per-BU incremental download loop (Download-Delta-{BU}/Unpack-{BU}/Re-Encode-{BU}, pulling from s3://{BU}_S3_BLOWER_SRC/{BU}/deltas/), uses diff_pid.csv/Merge-Diff-PID instead of full_pid.csv/Merge-Full-PID, and drops Count-Check and the entire delete-handling chain (Mirakl-Delete-Products, Mirakl-Taxonomy-Delete-*, Mirakl-Get-Deleted-Products) entirely — confirmed absent from the task list, not just dummied out.
  • delta-pre: same PREPROD/W1_PRE/DELTA_PRE pattern as full-pre vs full, applied to the delta skeleton.
  • The Schema-Check glob bug is confirmed isolated to jobs-inf-w1-full.py — both delta.py and delta-pre.py use the correct "w1_products.json" pattern with no stray backtick.

Cross-DAG file handoffs ​

FileProduced by (DAG:Task)Consumed by (DAG:Task)S3 pathConfidence
all_products.jsonecom_products_all:Upload-W1ecom_w1_full:Download-BU-Feeds (glob match) → Supply-Sync-AlgoliaW1_S3_ARCHIVE/w1_json_feeds/{target}/all_products.jsonConfirmed — Download-BU-Feeds's lpath=INTERMEDIATE_PATH places the file exactly where Supply-Sync-Algolia's bare-filename arg ('all_products.json') resolves; same file, not a separate copy.
w1_products.jsonecom_w1_full:Translate → Upload-W1-Feedjobs-shw-gm.py:Download-W1-Feed (pulls most recent, not necessarily same-run)W1_S3_ARCHIVE/W1/{target}.w1_products*.jsonConfirmed for the handoff mechanism; not confirmed whether jobs-shw-gm.py always gets the same-run file vs. a stale prior run.
shopware.tar.gzecom_w1_full:Pack-Shopware → Upload-ShopwareShopware (external, pulls per README)SHOPWARE_S3_ARCHIVE/shopware_import_filesConfirmed upload mechanism; Shopware-side pull not traced in this repo.
supply_sync.jsonecom_w1_full:Supply-Sync-Algolia → Transfer/Upload-Supply-SyncAlgolia (SUPPLY_SYNC_ALGOLIA_INDEX); S3 archiveSHOPWARE_S3_ARCHIVE/supply_sync_feedConfirmed.
{bu}_product.json (e.g. GM_product.json)Confirmed real and live — see note belowmerge_bu_erp/merge_bu_attrs (data-lineage.md §1/§2)w1_json_feeds/production/{BU}_product.jsonConfirmed via live task log — producer DAG/task not re-identified after an earlier false trace, but not pursued further per scope decision (see note).

Note: {bu}_product.json is confirmed real and live — an earlier pass this session was wrong ​

A Download-BU-Feeds task log from a production ecom_w1_delta run (s3://wwi.ops.production.airflow.logs/logs/dag_id=ecom_w1_delta/run_id=manual__2026-08-20T02:08:58.941190+00:00/task_id=Download-BU-Feeds/attempt=1.log) shows the task's aws s3 sync s3://watsco-aux/w1_json_feeds/production ... downloading all of the following in the same run: all_products.json and 18 per-BU files — BK, CE, CECA, CECPD, CEFL, CEMA, CEMX, CENE, CEPR, CESC, CESE, CEST, CIAC, EC, GM, HM, PP, TW (each as {BU}_product.json). This also confirms W1_S3_ARCHIVE resolves to the watsco-aux bucket.

An earlier pass this session concluded {bu}_product.json had no live producer, based on checking only jobs-alg-all.py (uploads only all_products.json), jobs-alg-{gm,ec,pp,bk}-inr.py (found w1_json_feeds references only inside dead/commented code), and all jobs-shw-*.py files (zero references) — and incorrectly generalized "not found in the files I checked" to "doesn't exist." The live log evidence overrides that conclusion. The actual producer DAG/task was not re-identified — with 98 jobs-*.py files in the repo and the confirmed candidates all ruled out, finding the real producer would require a broader systematic search that wasn't pursued, since it doesn't change anything about what W1 actually consumes (which is what this document tracks). If the producer matters for a future task, start from the per-BU jobs-alg-{bu}.py (non--inr) files, not the ones already ruled out.

Other anomalies found while building this catalog ​

  • Mirakl-Taxonomy-Delete-Push/Mirakl-Operator-Delete-Push are dummy operators in ecom_w1_full (real logic present but commented out directly below each). Their downstream *-Delete-Report tasks read tracking-ID files (mirakl_taxonomy_delete_tracking_ids.csv, mirakl_operator_delete_tracking_ids.csv) that are never written as a result — these report tasks likely run against stale or empty files every execution. Not confirmed whether this is an intentionally disabled feature or a bug.
  • Schema-Check's glob bug (jobs-inf-w1-full.py only): the pattern string is "w1_products.`json" — a stray backtick before json — which won't match the real file w1_products.json. json_utils.schema_check silently receives an empty file list for this task. Confirmed correct in jobs-inf-w1-delta.py/jobs-inf-w1-delta-pre.py.

Appendix: S3 buckets ​

The code never hardcodes bucket names — every S3 reference is built from environ['{VAR}'] (bucket) + a literal path fragment (see the tables above). These are genuine OS/MWAA environment variables, not Airflow Variables — the daily Airflow Variables snapshot at s3://watsco-aux/airflow_vars/production/{date}.json (848 keys, checked against 2026-08-19.json) does not contain any of the *_S3_SRC/*_S3_ARCHIVE/*_S3_ARGS names, so bucket names for most of these variables could not be resolved this session without direct MWAA environment-config access (not available under the enterprise-ro/enterprise-power profiles used — mwaa:ListEnvironments was denied under -ro, and further escalation wasn't attempted without asking first).

Confirmed bucket:

BucketConfirmed viaContents observed
watsco-auxDirect s3api list-objects-v2 (enterprise-ro); a full archived tarball download/extraction earlier this session; and a live Download-BU-Feeds task log (ecom_w1_delta, wwi.ops.production.airflow.logs bucket, account 184348296362, via ecom-logs-ops-power) showing the exact resolved command aws s3 sync s3://watsco-aux/w1_json_feeds/production ...watsco-aux = W1_S3_ARCHIVE, confirmed directly (not inferred from path-pattern matching, per the resolved bash command). Top-level prefixes: W1/, w1_json_feeds/, archive/, airflow_vars/ (daily Airflow Variables JSON snapshots), reports/, cache/, algolia_config/, hydros/, hydros_archive/, xref//XREF/, BRANCH_GEO/, BLOWER_TRIG_LOCKS/, and per-BU prefixes (BK/, CE/, CENE/, EC/, GM/, HM/, REFONLY/, csd/, _TMP/).

Task logs live in a separate bucket/account — wwi.ops.production.airflow.logs, in AWS account 184348296362 (not the 784596786944 account enterprise-ro/enterprise-power resolve to). Use the ecom-logs-ops-ro/ecom-logs-ops-power aws-vault profiles for that bucket; ro was denied GetObject by IAM, power worked. Listing (ListObjectsV2) worked under enterprise-ro/enterprise-power for watsco-aux directly, but reading individual objects there required enterprise-power (enterprise-ro was denied GetObject specifically, not just HeadObject).

Unconfirmed — environment variable name and code-referenced path only (bucket name not resolved; do not assume these all point to the same bucket, or to watsco-aux):

VariablePath fragment used in codeReferenced byPurpose (from code context)
ALL_S3_SRC(root)jobs-alg-all.py:Download-PIMSource for ALL/FULL PIM2 export zip
ALL_S3_ARCHIVE(root)jobs-alg-all.py:ArchiveArchive destination for staging/all_products.json + import dir
AUX_S3_SRC—referenced but not traced to a specific task this sessionUnclear — name suggests overlap with watsco-aux, not confirmed same bucket
BK_S3_SRC, BK_S3_ERP_SRC, BK_S3_ERP_PATHerp-data, STS-assumed-role pathjobs-alg-all.py:Download-BK-Regions-Map, Download-BK-ERP-DataBK's raw ERP/branch-map source (STS role — separate account/bucket from the default profile)
CE_S3_SRCerp-datajobs-alg-all.py:Download-CE-ERP-DataCE's raw ERP availability source
CEHO_S3_SRC—referenced, not traced to a specific task this sessionCEHO (Homans) equivalent of the above pattern
EC_S3_SRC, EC_S3_ERP_SRC, EC_S3_ERP_PATHSTS-assumed-role pathjobs-alg-all.py:Download-EC-ERP-DataEC's raw ERP source (STS role)
GM_S3_SRC, GM_S3_MINCRON_SRCmincronjobs-alg-all.py:Download-GM-ERP-DataGM's Mincron ERP export source
NEUCO_S3_SRC, NEUCO_S3_PIM_SRC—jobs-alg-all.py (NeuCo tasks, gated by Airflow Variable)NeuCo source — see opportunities.md, not reachable from W1
PP_S3_SRC, PP_AVAILABILITY_S3_SRC—jobs-alg-all.py:Download-PP-ERP-DataPP's raw ERP availability source
SHOPWARE_S3_ARCHIVEshopware_import_files, supply_sync_feedecom_w1_full:Upload-Shopware, Upload-Supply-SyncShopware import package + Supply-Sync Algolia feed archive destination
SS_S3_SRCpendingecom_w1_full:Download-SalesSales-quantity-shipped source feed
TE_S3_ERP_SRC—jobs-alg-all.py (TE availability conversion)TE's raw ERP source
*_S3_ARGS (one per BU + ALL/W1/SHOPWARE/SS)—passed as s3args to the shared template_download/template_upload bash templatesCLI flags (e.g. --recursive, profile/region overrides) for the corresponding _SRC/_ARCHIVE bucket's transfer — not a bucket identifier itself

To fully resolve this table: either (a) get MWAA environment-config read access (airflow:GetEnvironment or equivalent) and inspect the environment's variable definitions directly, or (b) pull a BashOperator task log for one of the Download-*/Upload-* tasks in the table above — Airflow logs the fully-rendered bash command, which would show the resolved bucket name in the aws s3 cp/sync invocation.