# Changelog of 1688 Supplier Finder — Bulk Supplier Shortlist & Comparison (`crawleast/1688-supplier-finder`) Actor

- **URL**: https://apify.com/crawleast/1688-supplier-finder/changelog.md
- **Full Actor documentation**: https://apify.com/crawleast/1688-supplier-finder.md

## Changelog

### 0.1.11 — Bulk-decision storefront positioning (2026-10-16)

Docs and Store text only; runtime behavior is identical to 0.1.10.

- Store title, SEO title, descriptions and the README opening describe bulk supplier comparison across many products. They no longer lead with photo lookup, factory-finder or Amazon FBA wording, which 1688 Image Search already uses, so the two Actors stop competing for the same searches.
- The README opening routes single-product lookups to 1688 Image Search and compares the fee with the manual effort it replaces, not with another Actor's per-row price.

### 0.1.10 — Durable compensation reservation (2026-10-16)

Private candidate; pricing, capacity and resources are unchanged.

- Reserve each compensation offer in the checkpoint before fetching. After migration, an uncertain or failed compensation is not attempted again; completed observations remain deliverable and failed enrichment remains unbilled.
- A failed reservation write sends no additional detail requests. SUMMARY reports checkpoint_failed rather than silently retrying.
- Tests cover migration immediately after reservation and after failed compensation, using the exact boundary snapshot, plus reservation-write failure.

### 0.1.9 — Lighter resume checkpoints (2026-10-15)

Private candidate; pricing, the 100-product limit, enrichment defaults, memory and timeout are unchanged. Supersedes build 0.1.11 (code 0.1.8), which was not sent to QA.

- Once a product's shortlist is selected, its resume checkpoint keeps only the shortlist and drops the full search candidate lists (up to 20 cards plus 20 filtered candidates per product). Selection never reads them again, including after a restart.
- Why: the 0.1.8 cloud run d2yjg85VW9l0P0QFr (100 photos) spent 371 of 1,397 seconds on 490 checkpoint writes, about 0.76 seconds each, mostly while delivering products after the detail phase.

### 0.1.8 — Detail compensation that actually runs (2026-10-15)

Private candidate; pricing, the 100-product limit, enrichment defaults, memory and timeout are unchanged.

- Correction to 0.1.7: its compensation gate measured the run-level slack, but the retry still ran on the already-expired detail-slice deadline and only started after every delivered product had been written, so it could not deliver. Cloud run NFaAcZar6P0pXDzjd shows the cause: both detail groups finished fetching about two minutes into the 330-second slice, then waited about six minutes for product delivery before the compensation check.
- Detail workers no longer wait for product delivery. Delivery continues in order in the background and is complete before the run summary.
- A group's failed offers are retried once on a fresh identity right after that group, inside the detail slice. Offers still failing after the slice get one final attempt in the run's remaining planned window, with its own deadline. Each offer is retried at most once, also across restarts.
- The compensation estimate scales with size: 45 seconds per group plus 3 seconds per offer, plus a 60-second margin.
- Resume-state writes that are requested while another write is in progress are merged into one follow-up write. SUMMARY.resumeCheckpoints reports write count, merged requests, failures and write time.

### 0.1.7 — MOQ-tier pricing and token-failure rescue (2026-10-15)

Private candidate; pricing, the 100-product limit, enrichment defaults, memory and timeout are unchanged.

- Unit price and landed cost use the MOQ-applicable current-table tier: the lowest specification price at the highest quantity threshold the MOQ can reach, instead of the cheapest bulk-quantity price across all breaks. Larger-quantity tiers stay in the price ladder.
- Detail offers that failed with token_expired or token_unrecoverable on a warm chain are now eligible for fresh-session rescue, even when the chain itself finished cleanly. Previously only page-death victims were rescued.
- Compensation retry uses the run-level remaining budget instead of the detail-slice deadline, so failed offers can be retried when the 330-second detail window is used up but the 2400-second platform timeout still has room.

### 0.1.6 — Cost basis and auditable price evidence (2026-10-15)

Private candidate; pricing, the 100-product limit, enrichment defaults, memory and timeout are unchanged.

- Landed-cost estimates use one source SKU's price and weight only when the minimum price is explicitly linked to that SKU. Otherwise rows say whether the estimate combines the minimum price with representative or inferred weight.
- Rows expose price basis, cost basis, weight basis, the weight used for shipping and, when unambiguous, the source SKU and sanitized specification.
- Each fetched detail offer gets a free PRICE_EVIDENCE\_<offerId> record with bounded, allowlisted price, MOQ, SKU, weight and capture-time fields. It does not store full HTML, authentication data, proxy settings or seller personal data.
- Documentation clarifies that bizType is a business-model label and does not override the separate 1688 isFactory flag.

### 0.1.5 — Subscription-tier billing and storefront completion (2026-10-15)

Private candidate; FREE prices, the 100-product limit, memory and timeout are unchanged. Apify builds 0.1.6 and 0.1.7 both contain code version 0.1.5; build 0.1.7 includes the complete SDK budget-edge correction.

- Main events receive subscription-tier discounts: BRONZE 10%, SILVER 20%, and GOLD/PLATINUM/DIAMOND 30%. The delivered-run base fee stays $0.01.
- Budget checks, checkpoint-ledger reconciliation and reported charge amounts use the actual per-event prices supplied for the run, rather than FREE-plan estimates.
- Fully charged requests do not mark the run quota-blocked merely because the SDK reports that another event of the same type no longer fits the remaining budget.
- Store SEO, categories and the API example input are synchronized; the example uses the validated two-photo sourcing workflow.

### 0.1.4 — Enforce validated first-release capacity (2026-10-15)

Private candidate for independent QA; no pricing or resource changes.

- The first release accepts at most 100 products. The default 100-product workload passed delivery, detail, pricing, schema and checkpoint-restart tests; the 250-product trial did not complete, so it is not offered.
- Both JSON and CSV lists over 100 are rejected before searching with INPUT_TOO_LONG and no charges. Lists are never silently truncated to fit the measured limit.
- The 400-detail request cap remains separate; higher enrichment depths require their own validation and may yield partial enrichment.

### 0.1.3 — Independent detail groups and restart telemetry (2026-10-15)

Private candidate; no pricing, resource or input-cap changes.

- Only the first primary detail group may reuse the verified cached identity. All subsequent and compensation groups bootstrap independent sessions, preserving the existing group quota and concurrency.
- Checkpoint resumes count separately from migration notices. API-triggered reboots that send SIGTERM without a migration event now report a checkpoint resume accurately.

### 0.1.2 — Preserve specification price entries (2026-10-15)

Private candidate; no price, input-capacity, memory or timeout changes.

- Price-table cleanup removes only identical quantity-and-price pairs. Different specification prices at the same quantity and entries with unknown quantity remain visible.
- Full parse → cost → sanitize → row regression tests cover both desktop and MTOP paths, preventing cleanup from silently undoing correct price parsing.

### 0.1.1 — Private capacity-test fixes (2026-10-15)

Private candidate only; capacity must be re-measured before publishing. No price, memory or platform-timeout changes.

- **First-wave scheduling:** after the opening canary succeeds, its remaining chunk runs alongside the other two chunks rather than serially ahead of them. Identity quotas, serial requests within each identity and cooldowns are unchanged.
- **Measured time-budget revision:** the prior 58-second wave estimate did not fit the 50-image samples. The revised estimate retains the 1,926-second image-phase safety cap and 2,400-second platform timeout; it does not claim a proven capacity.
- **Image-only first release:** title search delivered zero products in its private validation sample and is removed. Every JSON/CSV product needs an image URL; an optional title remains a reference label. Older failed calibration evidence is retained for traceability.
- **Current price priority:** displayed price-table values take precedence over non-promotional SKU fallback. The same validated prices supply the unit price, detail entries and landed-cost purchase-price calculation; repeated quantity thresholds are specification prices, not invented quantity discounts.

### 0.1.0 — Initial private build (2026-10-14)

Private build for internal testing; not published to the Store. The four pay-per-event events were registered with effect from 2026-10-14T14:34:42.224Z; the runtime checks their presence before charging. **Capacity limits are pending calibration:** no reliable per-run product limit is advertised yet.

- Migration checkpoints preserve finished searches, shortlisted candidates and enrichment without storing session credentials. Dataset-backed reconciliation avoids duplicate rows and charges after reboot.

- External image downloads validate and pin public DNS destinations, enforce redirect checks and a streamed 1 MiB limit, and preserve TLS certificate verification. Browser setup operations are bounded.

- Individual identities and unverified keyword storefront names are omitted. Empty or invalid prices cannot become verified prices; source flags retain unknown/null and parse Y/N correctly.

- CSV parsing rejects malformed quotes, duplicate alias columns and ragged rows, and protects serialized arrays from formula injection. Coverage follows minimum-match filtering; final ranks use current enriched values.

- **Product list in, flat supplier shortlist out.** Input is `products` (JSON items with `imageUrl` or `title`, plus optional `asin` and `clientSku`) or `productsCsv` (a pasted CSV with a header row; image/title/asin/sku column aliases are accepted). Each dataset item is one input product × one 1688 offer, with `inputIndex`, `clientSku` and `inputAsin` echoed for joins. The ASIN is a label only and is never looked up.

- **Supplier consolidation view (free).** `SUPPLIER_ROLLUP` (JSON) and `SUPPLIER_ROLLUP.csv` group delivered rows by supplier across the whole list: `matchedProductCount`, `matchedProductIndexes`, `offerCount`, unit-price range, lowest landed-cost estimate, `avgMatchScore`, `bestRank` and `catalogCoverage`. Every row also carries `supplierCatalogCoverage`. The rollup lives in the key-value store, never in the dataset, so it adds no billable rows.

- **Per-product status (free).** `INPUT_STATUS` (JSON) and `INPUT_STATUS.csv` list every input with its status (`OK`, `INVALID_PRODUCT`, `IMAGE_FETCH_FAILED`, `NO_MATCH`, `RISK_CONTROL`, `NETWORK`, `DEADLINE_SKIPPED`, `SKIPPED_QUOTA`) and the rows it delivered. Products without rows are not charged.

- **Shortlist controls.** `shortlistSize` (1–10, default 5), `enrichDepth` (0 to `shortlistSize`; defaults to 1 through `enrichDetails: true`), `maxResultsPerProduct` (1–20, default 20), `sortBy` (`landedCost` by default, or `price`, `supplierYears`, `matchScore`, `supplierCoverage`), `minMatchScore` (default 0), `supplierDedupe` (default on), `excludeSupplierIds` and `targetMarket`. With `sortBy: "landedCost"`, enriched rows come first by landed cost and the rest follow by unit price; `rankBasis` records the key that placed each row.

- **Matching.** Photos use 1688's reverse image search. Titles use English→Chinese keyword mapping plus 1688's keyword search; this path is experimental and less precise than photos. `matchScore` is a relative position score (1.0 = 1688's first result), not a similarity value.

- **Trust and disclosure.** Every row carries `enriched`, `priceVerified` (true only when the detail page confirmed the price), `detailLevel` (`list` / `detail` / `partial`), `scrapedAt` and `sourceUrl`. Decision fields (landed cost, weight, suggested retail, margin estimate, dropship score), compliance hints and the price ladder are filled on enriched rows only, by design. Known gaps are disclosed: no `reviewCount`/`rating`, `responseRate` usually null, `categoryId` without a category path. Personal data is excluded, and individual sellers have `companyName`/`supplierUrl` blanked.

- **Planned pricing (pay per event).** `run-base-fee` $0.01 (once, only when at least one row is delivered), `product-matched` $0.010 (per product with at least one row), `shortlist-row` $0.003 (per row) and `supplier-enriched` $0.002 (per row with a full detail). No start fee and no platform-usage pass-through. `maxTotalChargeUsd` defaults to $20 (minimum $0.04); products the cap cannot fully cover are skipped up front as `SKIPPED_QUOTA` and never charged. Worked example: 10 products × 5 offers with `enrichDepth` 1 = $0.28.

- **Zero-charge validation errors.** `NO_INPUT`, `INPUT_TOO_LONG`, `CSV_PARSE_FAILED` (echoes the detected columns) and `ENRICH_CAP_EXCEEDED` (products × `enrichDepth` above 400; the message suggests a valid configuration). Runtime outcomes are `QUOTA_BLOCKED`, `RISK_CONTROL_CANARY`, `CIRCUIT_BREAKER` and `PARTIAL_DEADLINE` (products skipped by the time budget are free).

- **Pipeline.** Built on the 1688 image-search pipeline, with a wave-aware search time budget, a detail budget sized to products × `enrichDepth`, and a hardened detail browser: if removing the bandwidth guard fails, it is retried once, and on a second failure the group fails as `guard_stuck` instead of fetching with the guard still attached.

- **Schemas and run options.** Dataset, key-value store (`SUMMARY`, `OUTPUT`, `SUPPLIER_ROLLUP`, `SUPPLIER_ROLLUP.csv`, `INPUT_STATUS`, `INPUT_STATUS.csv`) and output schemas are declared. Default run options: 2,400 s timeout and 1,024 MB of memory (fixed).
