diff --git a/API.md b/API.md
index d5f689d..ef31d6e 100644
--- a/API.md
+++ b/API.md
@@ -43,11 +43,13 @@ The main HTTP client for communicating with the Hellotext API.
Makes an HTTP request to the Hellotext API.
**Parameters:**
+
- `$method` - HTTP method (GET, POST, PATCH, PUT, DELETE)
- `$path` - API endpoint path
- `$data` - Request payload
**Returns:**
+
```php
[
'request' => [
@@ -61,6 +63,7 @@ Makes an HTTP request to the Hellotext API.
```
**Example:**
+
```php
use Hellotext\Api\Client;
use Hellotext\Constants;
@@ -85,6 +88,7 @@ $response = Client::post(Constants::API_ENDPOINT_PROFILES, [
Creates a client instance with a custom API suffix. Useful for testing.
**Example:**
+
```php
$client = Client::with_sufix('/v2');
```
@@ -110,10 +114,12 @@ Creates a new event tracker. If no session is provided, it attempts to retrieve
Tracks an event with the given action and payload.
**Parameters:**
+
- `$action` - Event action name (use constants from `Constants::EVENT_*`)
- `$payload` - Event data
**Example:**
+
```php
use Hellotext\Api\Event;
use Hellotext\Constants;
@@ -151,6 +157,7 @@ Accepts either a product ID or a `WC_Product` instance.
Returns the adapted product payload.
**Returns:**
+
```php
[
'reference' => int, // Product ID
@@ -168,6 +175,7 @@ Returns the adapted product payload.
```
**Example:**
+
```php
use Hellotext\Adapters\ProductAdapter;
@@ -192,6 +200,7 @@ public function __construct(\WC_Order $order)
Returns the adapted order payload.
**Returns:**
+
```php
[
'reference' => int, // Order ID
@@ -240,6 +249,7 @@ public function __construct(float|string|null $price)
Returns price in cents with currency.
**Returns:**
+
```php
[
'amount' => int, // Price in cents
@@ -260,6 +270,7 @@ public function __construct(?int $user_id, array $billing = [])
```
**Parameters:**
+
- `$user_id` - WordPress user ID (null for guest checkout)
- `$billing` - Billing data from checkout
@@ -268,12 +279,14 @@ public function __construct(?int $user_id, array $billing = [])
##### `process(): void`
Executes the profile creation/association flow:
+
1. Checks if profile exists
2. Creates new profile if needed
3. Associates profile with session
4. Updates session metadata
**Example:**
+
```php
use Hellotext\Services\CreateProfile;
@@ -315,6 +328,7 @@ Decrypts an encrypted session string.
Sets the `hello_session` cookie with proper expiration and security flags.
**Example:**
+
```php
use Hellotext\Services\Session;
@@ -384,6 +398,39 @@ Constants::EVENT_COUPON_REDEEMED // 'coupon.redeemed'
## WordPress Hooks
+The plugin registers these WooCommerce and WordPress hooks:
+
+| Hook | Handler | Purpose |
+| --------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| `woocommerce_after_single_product` | `hellotext_product_viewed` | Track `product.viewed` |
+| `woocommerce_after_cart` | `hellotext_trigger_cart_updated` | Compare current and previous cart state |
+| `woocommerce_add_to_cart` | `hellotext_trigger_cart_updated` | Compare current and previous cart state |
+| `woocommerce_cart_item_removed` | `hellotext_trigger_cart_updated` | Compare current and previous cart state |
+| `woocommerce_after_cart_item_quantity_update` | `hellotext_trigger_cart_updated` | Compare current and previous cart state |
+| `woocommerce_applied_coupon` | `hellotext_coupon_redeemed` | Track valid `coupon.redeemed` events |
+| `woocommerce_after_order_details` | `hellotext_order_placed` | Track `order.placed` and persist encrypted session metadata on the order; render-based hook, so duplicate prevention is required |
+| `woocommerce_order_status_changed` | `track_order_status` | Track `order.confirmed`, `order.cancelled`, and `order.delivered` |
+| `woocommerce_order_refunded` | `hellotext_refund_created` | Track `refund.received` |
+| `user_register` | `hellotext_user_registered` | Track customer registration/profile flow |
+| `hellotext_woocommerce_cart_updated` | `hellotext_cart_updated` | Track `cart.added` and `cart.removed` |
+| `update_option` | `custom_field_updated` | Recreate integration after Business ID changes |
+| `admin_init` | `hellotext_settings_init` | Register plugin settings |
+| `admin_head` / `wp_head` | `hellotext_script` | Inject frontend/admin scripts |
+
+## WooCommerce Compatibility
+
+Compatibility posture as of 2026-06-11. See [WooCommerce Compatibility and API Audit](docs/WOOCOMMERCE-AUDIT.md) for the full hook/API audit, HPOS assessment, compatibility matrix, and release recommendations.
+
+| Area | Status | Notes |
+| ------------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| WooCommerce order metadata | HPOS-aligned metadata access | Uses `WC_Order` metadata CRUD for Hellotext session metadata. |
+| Order status and refund events | HPOS-aligned metadata access | Reads session metadata from the order object instead of `get_post_meta()`. |
+| Product, cart, coupon events | Public hook/API usage | Uses WooCommerce hooks and objects, not order storage internals. |
+| Automated tests | Mock-backed unit coverage | Event tests cover outbound payloads without real WordPress/WooCommerce runtime or HPOS datastore coverage. |
+| Runtime verification | Required before declaration | Test with HPOS enabled and disabled in a real WooCommerce site before declaring formal compatibility in the plugin header/runtime. |
+
+Do not add a formal HPOS compatibility declaration until the release candidate has passed runtime checks on WooCommerce with HPOS enabled and disabled.
+
### Actions
#### `hellotext_create_profile`
@@ -391,9 +438,11 @@ Constants::EVENT_COUPON_REDEEMED // 'coupon.redeemed'
Triggers profile creation/association.
**Parameters:**
+
- `$payload` - Either user ID (int) or billing data (array)
**Usage:**
+
```php
// For logged-in user
do_action('hellotext_create_profile', $user_id);
@@ -431,6 +480,7 @@ The plugin respects the following environment variables:
- `HELLOTEXT_API_URL` - Override API URL (development only)
**Example (.htaccess):**
+
```apache
SetEnv APP_ENV development
SetEnv HELLOTEXT_API_URL https://api-dev.hellotext.com
@@ -454,6 +504,7 @@ try {
### Authentication
All API requests include an `Authorization: Bearer` header with either:
+
- **Business ID** for event tracking
- **Access Token** for profile/session management
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 10af1bb..f944d0d 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -46,6 +46,7 @@ composer install
```
This installs:
+
- Pest (testing framework)
- Mockery (mocking library)
- WordPress & WooCommerce stubs (for IDE autocomplete)
@@ -177,6 +178,35 @@ The project uses [Pest](https://pestphp.com/) for testing.
./vendor/bin/paratest
```
+The Composer aliases used by CI and maintainers are:
+
+```bash
+composer install
+composer test
+composer format:check
+composer build
+```
+
+`composer build` installs production dependencies with `--no-dev --optimize-autoloader`. Do not use the build output for local development without reinstalling dev dependencies afterward.
+
+### Verified Local Flow
+
+Last verified: 2026-06-11
+
+| Command | Result | Notes |
+| ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `composer install` | Passed | Requires PHP 8.2+ and Composer 2. The local system Composer emitted PHP deprecation notices under PHP 8.4; dependencies still installed correctly. |
+| `composer test` | Passed | Pest suite runs against WordPress/WooCommerce mocks, not a real WordPress install. |
+| `composer format:check` | Passed | PHP CS Fixer warned when run under PHP 8.4 because the Composer platform is PHP 8.2.12. CI runs the style check on PHP 8.2. |
+| `composer build` | Passed | Runs `composer install --no-dev --optimize-autoloader` and removes dev tooling from `vendor/`. Run `composer install` again afterward before continuing local development. |
+
+Setup assumptions:
+
+- PHP 8.2 or newer is available locally.
+- Composer can install from the checked-in `composer.lock`.
+- Tests do not require a real WordPress, WooCommerce, database, or Hellotext API connection.
+- Outbound HTTP in tests is mocked through WordPress HTTP function stubs.
+
### Writing Tests
#### Unit Tests
@@ -312,41 +342,45 @@ add_action('all', function($hook) {
### Pre-Release Checklist
-- [ ] Run all tests: `./vendor/bin/pest`
-- [ ] Check for PHP errors/warnings
-- [ ] Test on fresh WordPress installation
-- [ ] Test with WooCommerce latest version
-- [ ] Verify settings page functionality
-- [ ] Test event tracking in Hellotext dashboard
-- [ ] Update documentation if API changed
-- [ ] Update changelog.txt
+- [ ] Confirm the version in `hellotext.php` matches the release tag.
+- [ ] Update `changelog.txt` with user-facing changes, compatibility notes, and any known limitations.
+- [ ] Review open dependency PRs and confirm there are no urgent security/runtime updates pending.
+- [ ] Run `composer install` from a clean checkout.
+- [ ] Run `composer test` and confirm all tests pass.
+- [ ] Run `composer format:check` and confirm no style diff is reported.
+- [ ] Run `composer build` and confirm production dependencies install with `--no-dev --optimize-autoloader`.
+- [ ] Inspect the production `vendor/` tree enough to confirm dev-only packages such as Pest, Mockery, stubs, PHPUnit, and PHP CS Fixer are not included in the release build.
+- [ ] Verify `.distignore` excludes development files from the release zip: `.github/`, `tests/`, `composer.json`, `composer.lock`, `DEVELOPMENT.md`, and `API.md`.
+- [ ] Smoke test the plugin zip in a clean WordPress/WooCommerce site.
+- [ ] Confirm settings save correctly: Business ID, access token, webchat ID, placement, and behavior.
+- [ ] Confirm script/webchat injection renders on the storefront when configured.
+- [ ] Smoke test tracking for product view, cart add/remove, coupon redemption, checkout/order placement, order status change, refund, user registration, plugin activation, and plugin deactivation.
+- [ ] Smoke test classic template pages and WooCommerce Cart/Checkout blocks separately for product, cart, and checkout coverage.
+- [ ] Smoke test order placement, order status, and refund flows with WooCommerce HPOS enabled and disabled.
+- [ ] Record WooCommerce API/HPOS compatibility notes in the release description if anything changed.
+- [ ] Run `composer install` again after build validation to restore dev dependencies before more local work.
### Creating a Release
1. **Tag the release:**
+
```bash
git tag -a v1.3.0 -m "Release version 1.3.0"
git push origin v1.3.0
```
2. **Build release package:**
-```bash
+
+````bash
# Remove dev dependencies
composer install --no-dev
-# Create zip
-cd ..
-zip -r hellotext-wordpress-1.3.0.zip hellotext-wordpress \
- -x "hellotext-wordpress/.git/*" \
- -x "hellotext-wordpress/tests/*" \
- -x "hellotext-wordpress/node_modules/*"
-```
-
-3. **Create GitHub release:**
- - Go to Releases → Draft new release
- - Select the tag
- - Upload the zip file
- - Add release notes from changelog
+3. **Verify the generated release:**
+ - Download the release zip from GitHub.
+ - Confirm files excluded by `.distignore` are not included: `.github/`, `tests/`, `composer.json`, `composer.lock`, `DEVELOPMENT.md`, and `API.md`.
+ - Confirm runtime files are included: `hellotext.php`, `src/`, `vendor/`, `README.md`, and `changelog.txt` if present.
+ - Install the zip in a clean WordPress/WooCommerce site.
+ - Confirm the release asset installs and activates without PHP warnings in `debug.log`.
### Post-Release
@@ -354,18 +388,38 @@ zip -r hellotext-wordpress-1.3.0.zip hellotext-wordpress \
2. Announce release to team
3. Monitor error logs for issues
+### Dependency Update Triage
+
+Use this checklist before merging dependency-only PRs:
+
+- Confirm the diff is limited to dependency metadata or the expected workflow file.
+- Confirm GitHub reports the PR as mergeable.
+- Confirm required CI checks pass.
+- Prefer merging patch/minor test stub updates independently from runtime code changes.
+- For GitHub Action major updates, inspect the action release notes before merging.
+
+Dependency PRs reviewed during this maintenance pass:
+
+- Keep dependency-only lockfile refreshes separate from runtime compatibility changes.
+- Prefer one broad lock refresh over multiple older overlapping Dependabot PRs when it carries newer compatible versions and CI passes.
+- For GitHub Action major updates, inspect the action release notes and verify the generated zip on the next tagged release because release publishing only runs on tags.
+
+See also [WooCommerce Compatibility and API Audit](docs/WOOCOMMERCE-AUDIT.md) for hook, HPOS, and release compatibility notes.
+
## Contributing
### Workflow
1. **Fork & Clone**
+
```bash
git clone https://github.com/YOUR_USERNAME/hellotext-wordpress.git
cd hellotext-wordpress
composer install
-```
+````
2. **Create Feature Branch**
+
```bash
git checkout -b feature/my-new-feature
```
@@ -376,17 +430,20 @@ git checkout -b feature/my-new-feature
- Update documentation
4. **Run Tests**
+
```bash
./vendor/bin/pest
```
5. **Commit**
+
```bash
git add .
git commit -m "feat: add new feature"
```
Use [Conventional Commits](https://www.conventionalcommits.org/):
+
- `feat:` - New feature
- `fix:` - Bug fix
- `docs:` - Documentation
@@ -394,6 +451,7 @@ Use [Conventional Commits](https://www.conventionalcommits.org/):
- `refactor:` - Code refactoring
6. **Push & PR**
+
```bash
git push origin feature/my-new-feature
```
@@ -458,6 +516,7 @@ function hellotext_track_new_action($data): void {
**Issue**: Mockery errors or WordPress function not found
**Solution**: Ensure WordPress and WooCommerce stubs are installed:
+
```bash
composer require --dev php-stubs/wordpress-stubs php-stubs/woocommerce-stubs
```
diff --git a/README.md b/README.md
index 9f0a551..9e83b36 100644
--- a/README.md
+++ b/README.md
@@ -21,31 +21,31 @@ Download **[the latest version](https://github.com/hellotext/hellotext-wordpress
## Installation
-Visit the control panel of your WordPress site where you manage your WooCommerce store, and from the *Plugins* section, click on *Add New Plugin*.
+Visit the control panel of your WordPress site where you manage your WooCommerce store, and from the _Plugins_ section, click on _Add New Plugin_.
-At the top, click on *Upload Plugin*.
+At the top, click on _Upload Plugin_.
-Click the button to choose the Hellotext plugin you downloaded in *.zip* format from your files.
+Click the button to choose the Hellotext plugin you downloaded in _.zip_ format from your files.
-Select the file from where you downloaded it (typically in the *Downloads* folder) and confirm to upload it to WordPress.
+Select the file from where you downloaded it (typically in the _Downloads_ folder) and confirm to upload it to WordPress.
-Click on *Install Now* to install the plugin.
+Click on _Install Now_ to install the plugin.
-Once installed, click on *Activate Plugin* to activate it.
+Once installed, click on _Activate Plugin_ to activate it.
-Your plugin is now activated. You can check that it has been correctly installed and activated from *Plugins* by clicking on *Installed Plugins*.
+Your plugin is now activated. You can check that it has been correctly installed and activated from _Plugins_ by clicking on _Installed Plugins_.
You should see Hellotext as shown in the image below.
@@ -57,21 +57,21 @@ With the Hellotext plugin already installed, let's configure it.
The first thing you need to do is obtain your business identifier in Hellotext.
-From your Hellotext business panel, visit the *Settings* section and you will find it below your business name.
+From your Hellotext business panel, visit the _Settings_ section and you will find it below your business name.
Select and copy this identifier as you will need to add it to your WooCommerce site.
-From the *Extensions* section in your WooCommerce site, click on Hellotext.
+From the _Extensions_ section in your WooCommerce site, click on Hellotext.
-Paste the business identifier you copied from Hellotext into the *Business ID* field.
+Paste the business identifier you copied from Hellotext into the _Business ID_ field.
-Go back to Hellotext and, from the *Authorizations* section, click on *Create new token*.
+Go back to Hellotext and, from the _Authorizations_ section, click on _Create new token_.
@@ -83,9 +83,9 @@ Once created, click on the indicated icon to copy your created token.
-Now, go back to your WooCommerce panel and paste the authorization token into the *Access Token* field.
+Now, go back to your WooCommerce panel and paste the authorization token into the _Access Token_ field.
-Click on *Save Changes* to save the changes.
+Click on _Save Changes_ to save the changes.
@@ -117,12 +117,31 @@ For developers looking to integrate, extend, or contribute to this plugin:
- **[API Documentation](API.md)** - Complete API reference for classes, methods, and hooks
- **[Development Guide](DEVELOPMENT.md)** - Setup, testing, and contribution guidelines
-### Requirements
+- **[WooCommerce API Audit](docs/WOOCOMMERCE-AUDIT.md)** - Hook, HPOS, and compatibility audit notes
+
+## Requirements
- PHP 8.2 or higher
- WordPress 5.0 or higher
- WooCommerce 5.0 or higher
+## Compatibility Matrix
+
+| Layer | Declared support | Tested in CI | Notes |
+| ----------------- | ------------------------ | --------------------------------------------------- | ---------------------------------------------------------------------------------- |
+| PHP | 8.2+ | 8.2, 8.3, 8.4, 8.5 | Composer platform is pinned to PHP 8.2.12. |
+| WordPress | 5.0+ | Mock-backed unit tests only, no WordPress runtime | Runtime smoke testing is required before release. |
+| WooCommerce | 5.0+ | Mock-backed unit tests only, no WooCommerce runtime | HPOS, Cart/Checkout blocks, and block-theme behavior require manual smoke testing. |
+| WooCommerce stubs | Not a runtime dependency | Locked through Composer dev dependencies | Used for development/test confidence only. |
+| Release package | GitHub tag workflow | Tag workflow only runs on `v*` tags | Verify the generated zip contents before publishing. |
+
+## Compatibility Notes
+
+- Order session metadata uses WooCommerce order CRUD methods instead of direct post meta APIs.
+- Before each release, test event tracking with WooCommerce HPOS enabled and disabled.
+- Classic template hooks and WooCommerce Cart/Checkout blocks should be smoke tested separately.
+- Formal WooCommerce HPOS compatibility declaration is deferred until manual runtime smoke testing passes.
+
## Support
Need help?
diff --git a/docs/WOOCOMMERCE-AUDIT.md b/docs/WOOCOMMERCE-AUDIT.md
new file mode 100644
index 0000000..3f23d54
--- /dev/null
+++ b/docs/WOOCOMMERCE-AUDIT.md
@@ -0,0 +1,97 @@
+# WooCommerce Compatibility and API Audit
+
+Last reviewed: 2026-06-11
+
+This audit covers the WooCommerce and WordPress integration points used by the Hellotext WooCommerce plugin. It is intentionally maintenance-focused: it documents current hooks, validates order access against current WooCommerce HPOS guidance, and records practical release risks without changing Hellotext API contracts.
+
+## References
+
+- WooCommerce APIs overview:
+- WooCommerce block hooks reference:
+- WooCommerce HPOS extension recipe book:
+- WooCommerce Code Reference:
+- WordPress Plugin Handbook hooks and activation/deactivation APIs:
+
+## Summary
+
+The plugin uses WooCommerce PHP hooks and WooCommerce objects for the customer activity it tracks. Order session metadata access follows HPOS guidance where the plugin uses `wc_get_order()`, `$order->get_meta()`, `$order->update_meta_data()`, and `$order->save()` instead of direct post meta functions.
+
+This is not a full runtime compatibility claim. The current tests use mocks and do not prove behavior against a real WooCommerce HPOS datastore, Cart/Checkout blocks, or block themes.
+
+Do not declare HPOS compatibility yet. Add the declaration only after a manual runtime smoke test passes with WooCommerce HPOS enabled and disabled in a real WordPress/WooCommerce install.
+
+## Hook and API Matrix
+
+| Area | Hook/API | Handler | Current status | Notes |
+| --- | --- | --- | --- | --- |
+| Product views | `woocommerce_after_single_product` | `hellotext_product_viewed()` | Classic template coverage | This is a classic template hook. Confirm behavior with block themes before claiming full block-theme coverage. |
+| Cart updates | `woocommerce_add_to_cart` | `hellotext_trigger_cart_updated()` | Appropriate | Mutation hook is still a valid WooCommerce cart hook. Tests cover event creation with mocks. |
+| Cart removals | `woocommerce_cart_item_removed` | `hellotext_trigger_cart_updated()` | Appropriate | Tests cover `cart.removed` payloads. |
+| Cart quantity changes | `woocommerce_after_cart_item_quantity_update` | `hellotext_trigger_cart_updated()` | Appropriate | Tests cover quantity increases and no-op unchanged carts. |
+| Cart page diff check | `woocommerce_after_cart` | `hellotext_trigger_cart_updated()` | Works for classic cart template | This hook does not cover all Cart block render paths. Future block-specific coverage should be evaluated. |
+| Coupon redemption | `woocommerce_applied_coupon` | `hellotext_coupon_redeemed()` | Appropriate | Handler validates the coupon before sending `coupon.redeemed`; tests cover valid and invalid coupons. |
+| Order placement | `woocommerce_after_order_details` | `hellotext_order_placed()` | Render-based hook with duplicate risk | This hook can fire on thank-you/order details and My Account view-order screens. Add or keep an idempotency guard before relying on it, and evaluate `woocommerce_thankyou` for classic checkout plus `woocommerce_store_api_checkout_order_processed` for Checkout block support. |
+| Order status changes | `woocommerce_order_status_changed` | `track_order_status()` | Appropriate | Handler maps `processing`, `cancelled`, and `completed` to Hellotext events. Uses WooCommerce order object/meta access. |
+| Refunds | `woocommerce_order_refunded` | `hellotext_refund_created()` | Appropriate | Handler loads orders/refunds with WooCommerce APIs and reads session meta with `$order->get_meta()`. |
+| User registration | `user_register` | `hellotext_user_registered()` | Appropriate WordPress hook | Tests cover profile creation flow with mocked HTTP. |
+| Profile creation | `hellotext_create_profile` | Closure in `CreateProfile.php` | Internal plugin hook | Used by event flows to associate logged-in users/sessions. |
+| Plugin deactivation | `register_deactivation_hook()` and `hellotext_remove_integration` | `hellotext_deactivate()` and closure in `AppRemoved.php` | Appropriate WordPress APIs | Tests cover DELETE request shape for integration cleanup. |
+| Plugin uninstall | `register_uninstall_hook()` | `uninstall()` | Appropriate WordPress API | Cleans Hellotext options and WooCommerce API keys. |
+| Settings | WordPress Settings API | `src/Misc/Settings.php` | Appropriate | Stores Business ID, access token, and webchat options using WordPress options. |
+| Webchat injection | WordPress script/footer hooks | `src/Misc/Scripts.php` | Appropriate WordPress APIs | Verify rendered script manually in a real WordPress page during release smoke testing. |
+| WooCommerce API keys | Direct `$wpdb` against `{$prefix}woocommerce_api_keys` | `src/Events/AppInstalled.php`, `hellotext.php` uninstall | Acceptable exception | WooCommerce API keys are not order data and are not part of HPOS order storage. No WooCommerce CRUD replacement is required for this table access. |
+
+## HPOS Assessment
+
+WooCommerce HPOS changes where order data is stored. The HPOS recipe book recommends avoiding direct WordPress post/postmeta APIs for order data and using WooCommerce CRUD APIs instead.
+
+The plugin's order session metadata paths are aligned with that guidance:
+
+- `src/Adapters/OrderAdapter.php` loads numeric orders with `wc_get_order()`.
+- `src/Events/OrderPlaced.php` stores the Hellotext session with `$order->update_meta_data(Constants::META_SESSION, ...)` and `$order->save()`.
+- `src/Events/OrderStatus.php` reads the stored session with `$order->get_meta(Constants::META_SESSION, true)`.
+- `src/Events/RefundReceived.php` loads the order with `wc_get_order($order_id)` and reads session metadata with `$order->get_meta(Constants::META_SESSION, true)`.
+- The plugin does not query `shop_order` posts or use direct post meta functions for order session metadata.
+
+The test suite uses WordPress/WooCommerce mocks, so it validates code behavior and request shape but not a real HPOS datastore. Because of that, the formal declaration is deferred.
+
+## Runtime Gaps To Smoke Test
+
+- Classic product page and block-theme product page behavior.
+- Classic cart page and Cart block behavior.
+- Classic checkout and Checkout block order placement behavior.
+- Duplicate `order.placed` prevention when an order details page is revisited.
+- HPOS enabled and disabled order placement, status change, and refund flows.
+
+## Deferred HPOS Declaration
+
+After a real WooCommerce smoke test passes with HPOS enabled and disabled, add this to the main plugin file:
+
+```php
+add_action('before_woocommerce_init', function () {
+ if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
+ \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
+ }
+});
+```
+
+Do not add this declaration until manual runtime testing confirms product view, cart, coupon, checkout, status transition, refund, settings, deactivation, and webchat behavior in a real store with HPOS both enabled and disabled.
+
+## Compatibility Matrix
+
+| Layer | Declared | CI-tested | Stub-tested | Assumed or manual-only |
+| --- | --- | --- | --- | --- |
+| PHP | README requires PHP 8.2+; Composer platform is PHP 8.2.12 | GitHub Actions runs Pest on PHP 8.2, 8.3, 8.4, and 8.5; format check on PHP 8.2 in CI | Not applicable | Local contributor machines may run newer PHP; PHP CS Fixer warns when run outside project platform. |
+| WordPress | README requires WordPress 5.0+ | No full WordPress runtime in CI | `php-stubs/wordpress-stubs` is locked to 6.9.x after dependency refresh | Runtime compatibility with each supported WordPress version is manual-only. |
+| WooCommerce | README requires WooCommerce 5.0+ | No full WooCommerce runtime in CI | `php-stubs/woocommerce-stubs` is locked to 10.x after dependency refresh | HPOS, Cart/Checkout blocks, and block themes require manual smoke testing. |
+| Hellotext API | Existing plugin endpoints and payload contracts | HTTP calls are intercepted in tests | Not applicable | Real API authentication and dashboard ingestion require staging/production smoke testing. |
+| Release package | `.github/workflows/release.yml` builds tag-triggered zip | Release workflow runs only on `v*` tags | Not applicable | Zip contents and installability must be checked after each tagged release. |
+
+## Recommendations
+
+1. Keep the HPOS declaration deferred until manual smoke testing passes with HPOS on and off.
+2. Add or keep an idempotency guard around `order.placed` while `woocommerce_after_order_details` remains the hook.
+3. Evaluate replacing `woocommerce_after_order_details` with a checkout-completion hook such as `woocommerce_thankyou` for classic checkout, while separately evaluating Checkout block coverage through Store API hooks.
+4. Document classic-template limitations for `woocommerce_after_single_product`, `woocommerce_after_cart`, and any block-theme gaps in release notes until runtime coverage is confirmed.
+5. Consider migrating cart diff state from `$_SESSION` to WooCommerce session storage (`WC()->session`) in a future compatibility-focused PR.
+6. Continue using WooCommerce CRUD methods for all future order/refund metadata reads and writes.