# Website-instance implementation handoff

## Current scope

The feed server now provides validated Autocraft XML converted to Agenty-compatible CSV. The website application and database were not available here; database reconciliation and stock removal are NOT implemented or tested by this change.

Expected URLs, using the same host as Agenty (verify the web-server routing from the website instance):

- Index: `https://ftpfeeds.campers4sale.co.uk/xml_feed_importer/json/autocraftmotorcaravans_csv_file_list.json`
- CSV: `https://ftpfeeds.campers4sale.co.uk/xml_feed_importer/uploads/xml_csv/autocraftmotorcaravans/YYYY/MM/FILENAME.csv`
- Manifest: CSV URL plus `.manifest.json`

Read the index once and pin that immutable batch throughout the import. Derive YYYY/MM from the basename. Verify the manifest reference, filename, complete=true, schema version, SHA-256 of downloaded CSV bytes, and nonzero parsed record count against row_count. Count CSV records with a CSV parser, not lines; descriptions contain embedded newlines. Download failure, missing manifest, checksum mismatch, bad headers, missing required values, duplicate IDs/URLs, or partial processing must prevent deactivation.

## Dealer resolution and database changes

1. Use the configuration key to query `dealer_feed_reference` and require exactly one dealer. The sample key is `autocraftmotorcaravans`;. align config/URLs with the actual database. Do not create a new dealer automatically or trust a CSV-provided numeric dealer ID.
2. Preserve the existing site's field mapping and update-or-insert behavior. Identity is `dealer_id + advert_url`, with XML `vehicle_id` stored separately as the external source ID. Do not match by title, price, or external ID alone. Do not alter advert URLs during price updates.
3. Inspect existing Autocraft records before switch-over: the historical CSV sample has the shared `/van-sales/` URL and a generated `year-price-index` vehicle ID. These do not match the new per-product URLs/XML IDs. Determine whether the site derived correct product URLs elsewhere. If necessary, reconcile legacy rows once using reviewed matches before enabling the new import. Never blindly treat the new identities as all-new vehicles.
4. Validate the complete batch before mutation. Serialize imports per dealer, including any remaining legacy importer. Stage the incoming keys and mapped rows; use bulk matching/upserts against existing dealer-scoped adverts. Retain the site's existing business rules and side effects. A unique `(dealer_id, advert_url)` constraint is desirable after checking existing duplicates and the database's index/collation constraints.
5. Track XML feed ownership explicitly using the site's existing source marker or an appropriate schema addition. Manual adverts and other dealer/source stock must be excluded from removal. Mark reviewed legacy rows as feed-managed during migration where appropriate.
6. Only after every row succeeds, bulk deactivate absent adverts using an anti-join/NOT EXISTS against the staged batch, scoped to this dealer and XML feed ownership, setting `advert_status=0`. Do not issue a separate lookup/removal query per vehicle. Apply upserts and deactivation in one database transaction when supported. Roll back all changes if any row, side effect required for import success, or finalization fails.
7. Empty batches are rejected by the feed server and must also be rejected by the website. Add a configurable suspicious-drop threshold against the last successful stock count; hold deactivation for manual review when exceeded. A count-consistent XML response alone cannot prove provider completeness. Keep the previous successful batch state on failure. Missing vehicles in a valid ordinary batch should deactivate; returning vehicles should reactivate under the site's existing rules.
8. Persist successful batch ID/hash and generation time. Ignore already imported batches and reject older batches so retries or overlapping jobs cannot revert current stock. Advance this state only after database commit. Log fetched, validated, inserted, updated, unchanged, deactivated, rejected and failed counts, plus dealer and batch identity.

## Switch-over

Prepare and test website import and stock-removal exclusion first. Pause the old Autocraft Agenty agent in Agenty, and disable its feed mapping/ingestion so delayed callbacks cannot affect the dealer. The feed-server agent mapping currently links `avpuabzyff` to `autocraftmotorcaravans`; other agents must continue operating. Exclude this dealer's XML-managed stock from the existing stock-removal cron, or make the cron defer to this completed-batch reconciliation. Drain any running legacy jobs before enabling XML imports.

Then resolve/reconcile legacy identities, confirm the dealer reference, import a reviewed full XML CSV, verify counts/URLs/prices, and enable the XML publisher and website schedules. The feed-server publisher uses separate paths so pre-switch-over runs do not overwrite Agenty's index. No scraper or stock-removal job was disabled during feed-server development.

## Required website tests

- Changed price on same dealer+URL updates existing row without duplicate.
- New URL inserts once; replay of the same batch is idempotent.
- Same URL under a different dealer does not update/remove that dealer's stock.
- Missing URL in a completed batch deactivates only XML-managed stock for this dealer.
- Returning vehicle reactivates correctly.
- Duplicate CSV URLs or XML IDs reject the batch before mutation.
- Download, checksum, parse, count, row-validation, database and mid-import failures retain stock and successful batch state.
- Empty and suspiciously reduced batches never trigger removal.
- Concurrent and stale batches cannot undo a newer import.
- Manual stock and excluded sources are unaffected; legacy removal cron cannot race XML reconciliation.
- Legacy Autocraft identities are reconciled without duplicating existing stock.

Feed-server tests cover field mapping, changed prices, new/missing CSV rows, duplicate/partial/empty rejection, CSV escaping, checksums and preservation of the previous publication on failure. They do not establish any database behavior.
