Skurail for WooCommerce: changelog

Every release's detailed notes. readme.txt carries the summary WordPress.org shows.

= 0.2.0 =
* Account gate (docs/47 §2): with no Skurail account linked to the store, nothing functional runs.
  Every skurail/v1 route but /system/register refuses with the opaque 401 (and the inbound probe
  with it); the shutdown ping, the durable-queue flush, the heartbeat, plan status and order relays
  send nothing; the recurring sync jobs are unscheduled (the daily buyer-PII purge stays); product
  fields, the listing box and the "Listed on" column are not registered; the optional module stays
  off; and WooCommerce → Skurail is the Connect page alone. Every admin-post handler but Connect,
  Connect with key and Sign out checks the nonce, then manage_woocommerce, then the link, and refuses
  with a message and a link to Connect (AJAX callers get JSON). The stock veto on orders Skurail
  already created stays, so WooCommerce never takes their stock twice.
* Embedded sign-up and sign-in (docs/47 §3): "Create a free Skurail account" and "Sign in to
  Skurail" announce a store-initiated pairing server-to-server as Connect did, then frame Skurail's
  own page (https://skurail-app.bekservice.uk/embed/woocommerce/connect) for the pairing on the
  Connect page instead of redirecting the browser away. Sign-up, email verification, sign-in and
  password reset happen there, protected by Cloudflare Turnstile verified by Skurail Cloud; the store
  is linked only by Skurail's signed /system/register callback after a verified sign-in. The
  plugin's own script (assets/js/connect.js) only resizes the frame and reloads the page when told
  the store is linked, accepting messages from the frame and Skurail's origin only. The
  SKURAIL_APP_BASE constant points the frame at a staging app.
* /system/register takes an optional `account_email`, stored with the connection for display
  ("Signed in to Skurail as ...") and never used by the gate. Disconnect is now "Sign out and
  disconnect".

= 0.1.4 =
* Plan status: the WooCommerce → Skurail page shows the plan, this billing period's marketplace orders
  against the allowance, any orders held for the plan limit, and a link to manage the plan. Fetched
  out of band through Action Scheduler (`GET /hooks/woocommerce/{id}/status`, signed, at most hourly,
  kept out of the delivery-health marker); the page only reads the stored answer, keeps the last one,
  dated, when Skurail cannot be reached, and gates nothing on it.
* Timeouts: outbound calls to Skurail wait by who is waiting. The connection test, disconnect and
  connection-key activation wait 10 s (the 2 s every call shared failed healthy stores whose round
  trip through a CDN takes 1-5 s); out-of-band sync (Action Scheduler flushes, order updates, the
  heartbeat) waits 8 s; sends made inside a web request (the shutdown ping, deactivation and
  uninstall notices) stay at 3 s, and a shutdown ping that runs out spills to the durable queue.
* Product import (off by default): with the new "Allow Skurail to create and update products"
  switch on, Skurail can create attributes, categories, products and variations from marketplace
  listings you confirm, update the fields you chose it to manage (never over an edit you made since),
  and import listing images within limits a shared host can afford. Nothing is ever deleted.
* Safe Switch: products show which marketplace listings they are linked to (a box on the product
  screen and a "Listed on" column).
* Orders: marketplace refunds are recorded on Skurail orders, and closed marketplace orders can be
  imported for your records without moving stock, sending emails or storing the buyer's address.
* Pairing: Connect now announces the pairing to Skurail server-to-server before it sends you to the
  consent screen, and the consent link carries only a one-time reference, never the secret.
  Completing the pairing requires HTTPS plus that reference and secret; it works once, and an
  identical retry is answered rather than refused. Shared keys must be at least 32 bytes, and
  re-pairing drops the previous connection's rotation-overlap key.
* Security: a key that has been rotated out can no longer rotate the key again; signatures now cover
  the request's query string, so paged catalogue exports are signed end to end; stores on plain
  permalinks (`?rest_route=`) verify; outbound calls to Skurail no longer follow redirects.
* Stock: Skurail can update stock for every SKU WooCommerce accepts, including SKUs with spaces,
  `#`, `+` or accented letters, through a new JSON-body form of the stock route.
* Export (schema 5): adds weight and dimension units, the tax basis, sale dates and per-variation
  status, backorders and image. A variation without its own SKU is no longer exported under its
  parent's SKU.
* Listing quantity: a store-wide setting and a per-product field on the Inventory tab cap the
  quantity Skurail lists on a marketplace while the store keeps selling its real stock (export
  schema 4 `max_qty`; shipped after the 0.1.3 notes were written).
* Privacy: the WordPress personal-data exporter and eraser now find the orders Skurail created, and
  a daily job removes marketplace buyer details 30 days after an order is completed or closed.
* Health: the heartbeat names the site it comes from, so a staging copy of a store can be told
  apart from production.
* Uninstall: cleanup and the "connection gone" notice to Skurail now run in released builds (they
  used to need a development checkout's autoloader).
* WordPress.org readiness: database queries use prepared identifiers, the readme's short description
  fits on one line, and the package no longer ships a hidden file.
* Orders: an order is refused, rather than created wrong, when its currency is one WooCommerce does
  not know or a line has no whole quantity of at least one (a cancelled marketplace item no longer
  becomes a unit to ship). An order a failed push left half-built is rebuilt by the retry; a re-sent
  order the merchant trashed is not created twice; a delivery address the marketplace releases later
  is filled in. Marketplace tax is a proper tax row, VAT-inclusive prices are split so the rows add
  up to what the buyer paid, a free shipping service keeps its name, and the new-order email goes
  out with the final totals.
* Partial shipments: when a marketplace order ships in several parcels, each parcel's tracking is
  recorded and the order is completed only when the last parcel ships.
* Marketplace changes reach the store: a cancelled marketplace order is cancelled in WooCommerce
  (unless it has shipped), and an eBay account deletion erases that buyer from their orders. An
  erased buyer's details are never written back by a later update of the same order.
* Order updates to Skurail: tracking added after an order is completed, and partial refunds (with
  the "restock" choice), are now sent; re-saving orders created before this release no longer sends
  their tracking a second time.
* Stock: stock updates can name the quantity they expect, so a sale Skurail has not yet seen is not
  overwritten; editing a Skurail-managed order in the admin never moves stock twice.
* Privacy: WooCommerce Analytics' copy of a guest buyer's name and address is now erased too on
  WooCommerce 8.2 to 9.3.
* Reliability: no duplicate scheduled jobs when two requests start at once, a lost Skurail table is
  recreated automatically, and queued changes are no longer re-sent when the database and web
  server clocks differ.
* Admin: the connection warning appears only after repeated failures (or a day-long one on a quiet
  store) and says why the last call failed; the Skurail page shows that failure too; the Connect
  screen says what is sent and that nothing is sent until you approve. Changing the store's
  currency, units, tax or stock settings now tells Skurail to refresh every listing.
* Compatibility: tested up to WordPress 7.1 and WooCommerce 11.2 (the integration matrix's current releases).

= 0.1.3 =
* Orders: persist the channel-order id immediately on create so a mid-create failure can't orphan an
  order and cause a duplicate on retry; record negative marketplace fee lines (discounts) instead of
  silently dropping them; serialize reverse-fulfilment under a per-order lock so concurrent
  multi-package shipments can't lose tracking; only fulfil Skurail-originated orders; clearer note on a
  status-only fulfilment.
* Inbound stock: write a cross-channel decrement at the level that actually owns the SKU's pool
  (parent vs variation) so parent-pooled inventory is no longer corrupted (oversell risk).
* Reliability: the durable change-queue now retries with exponential backoff and drops exhausted rows
  (was fixed-cadence with an unused `attempts` column); deactivation now cancels in-flight
  order-update retries (the per-hook empty-args unschedule never matched them).
* Security/handshake: validate the register callback's fields before storing and consume the pairing
  nonce only on success, so a malformed callback no longer clobbers a live connection or burns the
  nonce; fix the Connect button (wp_safe_redirect was silently bouncing the consent redirect back to
  wp-admin); garbage-collect the rotated-out previous key once its overlap window ends.
* Admin: surface repeated ping failures as a dismissible admin notice; validate the order-create /
  order-fulfil status settings against registered WooCommerce statuses.
* Export: count private/disabled variations in the manifest so totals match what is streamed; disable
  PHP SAPI gzip so the NDJSON stream isn't double-compressed; raise memory within the export request.
* Multisite: uninstall now removes tables, options, and the shared key on every site, not just one.
* Dev/tests: `composer test` runs the pure-PHP unit suite (no longer fatals without WordPress) and a
  new `composer test:integration` runs the WP-scaffolded suite. (The e2e harness's sign.sh signed
  the route only, so paged export requests failed verification; since 0.1.4 it signs the route and
  the canonical query.)

= 0.1.2 =
* Export (schema v3): resolve global/taxonomy attributes to human label → value names (was raw
  `pa_*` keys with term IDs/slugs); emit hierarchical category paths ("Parent/Child"); report an
  `untracked` stock mode for variations that manage no stock and whose parent manages none (was
  mislabelled `parent`); add `stock_status`/`backorders`, product-line `weight`/`dimensions`, and
  `regular_price`/`sale_price`. `price` is now the active selling price (respects sales).
* Export: the targeted /products/{id} and /products/batch pulls now carry the full description +
  short description body (bulk export stays compact, hash-only).
* Orders: don't relay Skurail's own create-time order status back to Skurail — an
  `order_create_status` of `completed` no longer echoes a spurious fulfilment event, and
  `cancelled`/`refunded` no longer trigger an erroneous cross-channel stock restore.
* Security: a replayed (captured) request is now rejected before it can consume the per-connection
  rate-limit budget, so it can't 429 legitimate Skurail traffic.
* Reliability: the durable change-queue now preserves change-kind priority on coalesce, so a later
  cosmetic edit can't downgrade a queued stock/delete signal.

= 0.1.1 =
* Security: enforce HTTPS on outbound Skurail calls (staging override via SKURAIL_API_BASE only);
  reject non-https base URLs at registration.
* Security: per-connection inbound rate limit; accept the previous shared key during a brief
  rotation overlap window so in-flight requests aren't dropped.
* Orders: Skurail-managed stock now blocks WooCommerce reduce *and* restore for the whole order
  lifecycle (fixes a double-decrement on later status changes); import marketplace
  shipping/fees/tax as line items; unmatched SKUs become visible placeholder lines + an order note.
* Change capture: also hook `pre_post_update` (importer batch writes) and `untrashed_post`
  (product restored from trash).
* Export: stream gzip incrementally (no full-page buffering); emit `brand` and per-record
  `schema_version` (record schema bumped to v2); fix `modified_gmt` to true UTC.
* Health: `/system/health` now reports order count, last error, and capabilities.
* Products: ETag/If-None-Match on the per-product fetch; configurable batch cap.
* Admin: connection-test button, store-local settings form, expanded diagnostics, recent log tail.
* Tests: order-idempotency unit test; real variable-product stock-mode integration tests;
  test-integration.sh now runs the suite when the WP test scaffolding is present.

= 0.1.0 =
* Initial skeleton: pairing/HMAC auth, REST surface, change capture, export, order push.
