@sadrmanesh/commerce-agent-skills 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +21 -1
- package/package.json +1 -1
- package/rules/order-and-webhook-reliability.md +1 -0
- package/skills/b2b-pricing/SKILL.md +1 -1
- package/skills/catalog-import-and-migration/SKILL.md +1 -1
- package/skills/checkout-ux-and-conversion/SKILL.md +1 -1
- package/skills/conversion-event-tracking/SKILL.md +1 -1
- package/skills/ecommerce-seo-structured-data/SKILL.md +1 -1
- package/skills/fraud-and-risk-basics/SKILL.md +1 -1
- package/skills/google-merchant-product-feeds/SKILL.md +1 -1
- package/skills/headless-nextjs-storefront/SKILL.md +1 -1
- package/skills/inventory-and-multi-warehouse/SKILL.md +1 -1
- package/skills/inventory-and-multi-warehouse/scripts/allocate-stock.mjs +16 -2
- package/skills/iranian-payment-gateways/SKILL.md +1 -1
- package/skills/marketplace-search-and-ranking/SKILL.md +1 -1
- package/skills/mcp-store-tooling/SKILL.md +1 -1
- package/skills/multi-vendor-payouts/SKILL.md +1 -1
- package/skills/order-and-webhook-reliability/SKILL.md +5 -1
- package/skills/order-and-webhook-reliability/evals/examples/redelivery-after-timeout.md +3 -0
- package/skills/order-and-webhook-reliability/evals/tasks.json +15 -0
- package/skills/payment-gateway-integration/SKILL.md +1 -1
- package/skills/product-catalog-modeling/SKILL.md +1 -1
- package/skills/product-description-guardrails/SKILL.md +1 -1
- package/skills/returns-and-refunds/SKILL.md +1 -1
- package/skills/rtl-and-persian-localization/SKILL.md +1 -1
- package/skills/shopify-app-development/SKILL.md +1 -1
- package/skills/storefront-accessibility/SKILL.md +1 -1
- package/skills/storefront-performance/SKILL.md +1 -1
- package/skills/stripe-payments/SKILL.md +1 -1
- package/skills/subscription-billing/SKILL.md +1 -1
- package/skills/tax-and-shipping/SKILL.md +1 -1
- package/skills/vector-product-search/SKILL.md +1 -1
- package/skills/woocommerce-plugin-development/SKILL.md +1 -1
- package/skills/woocommerce-rest-api/SKILL.md +1 -1
- package/skills/wordpress-plugin-security/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.3.3] - 2026-11-04
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `allocate-stock.mjs` could oversell when an order had two lines for the same SKU. The one-location check looked at each line alone, so two lines of 3 both fit a location with 4 free. It now sums quantities per SKU first.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `reserve()` in `allocate-stock.mjs`, a check-and-set step that reserves stock only when enough is free. The allocator uses it.
|
|
14
|
+
- Tests for two concurrent checkouts racing for the last unit, plus a test that runs the same race against a read-then-write reserve and expects it to oversell, so the race test shows it can fail. Removing the stock decrement, the availability check, or the per-SKU sum now fails the suite; before this change, removing the decrement still passed.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- The package and every skill are now version 0.3.3.
|
|
19
|
+
|
|
20
|
+
## [0.3.2] - 2026-10-31
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- A `redelivery-after-timeout` eval task and worked example for `order-and-webhook-reliability`. The handler stored and applied the event, the sender timed out before the 2xx arrived and redelivered, and the answer must return 2xx without repeating a side effect such as a second fulfillment, email, or stock decrement. The skill explains this case, its verification list and gotchas cover it, and the rule has a matching line.
|
|
25
|
+
- The skills, rules, and evaluation tasks as a Hugging Face dataset, <https://huggingface.co/datasets/sadrmanesh/commerce-agent-skills>, with a `skills` table, an `evals` table, and the installable folders. The README, the docs home page, and the installation guide link to it.
|
|
26
|
+
- `.github/workflows/mirror.yml`, which mirrors `main` and tags to `gitlab.com/sadrmanesh/commerce-agent-skills` when the `GITLAB_TOKEN` secret is set, and skips cleanly otherwise.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- The package and every skill are now version 0.3.2.
|
|
31
|
+
|
|
3
32
|
## [0.3.1] - 2026-10-30
|
|
4
33
|
|
|
5
34
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Skills and rules for AI coding agents that build e-commerce and marketplace products.
|
|
4
4
|
|
|
5
|
-
**Documentation:** <https://sadrmanesh.github.io/commerce-agent-skills/>
|
|
5
|
+
**Documentation:** <https://sadrmanesh.github.io/commerce-agent-skills/> · **Dataset:** [Hugging Face](https://huggingface.co/datasets/sadrmanesh/commerce-agent-skills)
|
|
6
|
+
|
|
7
|
+
**Read more:** [Treat agent skills like code: evals, a strict validator, and an npx installer for commerce skills](https://dev.to/sadrmanesh/treat-agent-skills-like-code-evals-a-strict-validator-and-an-npx-installer-for-commerce-skills-13dk) on Dev.to · [The knowledge your senior engineers carry, packaged for coding agents](https://www.linkedin.com/pulse/knowledge-your-senior-engineers-carry-packaged-coding-sadrmanesh-xtenf/) on LinkedIn
|
|
6
8
|
|
|
7
9
|
The skills follow the [Agent Skills](https://agentskills.io/specification) format: a folder, a `SKILL.md` file with `name` and `description`, and optional reference files and scripts. An agent that supports that format can load them. The format does not name an install path. [Installation](docs/installation.md) explains how to point your agent at these folders.
|
|
8
10
|
|
|
@@ -132,6 +134,24 @@ Later releases can add:
|
|
|
132
134
|
|
|
133
135
|
See [CONTRIBUTING.md](CONTRIBUTING.md). Run the validator before opening a pull request. New skills need a matching rule, a row in `skills/catalog-order.txt`, and a place in `website/categories.json`.
|
|
134
136
|
|
|
137
|
+
## Technical report
|
|
138
|
+
|
|
139
|
+
[](https://doi.org/10.5281/zenodo.23883598)
|
|
140
|
+
|
|
141
|
+
commerce-agent-skills: Architectural Specification, Domain Constraints, and Evaluation Framework for E-Commerce AI Coding Agents. M. N. Sadrmanesh, Zenodo, 2026. <https://doi.org/10.5281/zenodo.23883598>
|
|
142
|
+
|
|
143
|
+
```bibtex
|
|
144
|
+
@techreport{sadrmanesh2026commerceagentskills,
|
|
145
|
+
author = {Sadrmanesh, Mohammad Naser},
|
|
146
|
+
title = {commerce-agent-skills: Architectural Specification, Domain Constraints, and Evaluation Framework for E-Commerce AI Coding Agents},
|
|
147
|
+
institution = {Zenodo},
|
|
148
|
+
year = {2026},
|
|
149
|
+
version = {1.0},
|
|
150
|
+
doi = {10.5281/zenodo.23883598},
|
|
151
|
+
url = {https://doi.org/10.5281/zenodo.23883598}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
135
155
|
## License
|
|
136
156
|
|
|
137
157
|
[MIT](LICENSE). Copyright (c) 2026 Naser Sadrmanesh.
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- Fetch the order or payment when the event is out of order or incomplete. Do not apply an older status over a newer one.
|
|
7
7
|
- Compare paid amounts in integer minor units.
|
|
8
8
|
- Make the paid transition idempotent so stock and receipt email run once.
|
|
9
|
+
- Treat a redelivery after a timeout as a duplicate: if the inbox row is already `applied`, return 2xx and run no side effect again.
|
|
9
10
|
- Write outbound webhooks from an outbox in the same transaction as the order change.
|
|
10
11
|
- Acknowledge a WooCommerce activation ping with 2xx and do not create an order from it.
|
|
11
12
|
- Return a retryable error only when the event was not stored. WooCommerce disables a webhook after more than five consecutive failures.
|
|
@@ -13,6 +13,17 @@ export function availableAt(location, sku) {
|
|
|
13
13
|
return Math.max(0, (level.onHand ?? 0) - (level.reserved ?? 0) - (level.safetyStock ?? 0));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Reserves quantity units of sku in levels as one check-and-set step, so nothing can run
|
|
18
|
+
* between reading what is free and writing the new reserved count. Returns false and
|
|
19
|
+
* changes nothing when fewer units are free. A database version is one conditional UPDATE.
|
|
20
|
+
*/
|
|
21
|
+
export function reserve(levels, sku, quantity) {
|
|
22
|
+
if (availableAt({ levels }, sku) < quantity) return false;
|
|
23
|
+
levels[sku] = { ...levels[sku], reserved: (levels[sku].reserved ?? 0) + quantity };
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
/**
|
|
17
28
|
* Returns { allocations: [{ sku, location, quantity }], shortfalls: [{ sku, quantity }], locations }.
|
|
18
29
|
* Never allocates more than a location has available.
|
|
@@ -27,7 +38,10 @@ export function allocateOrder(order, stock) {
|
|
|
27
38
|
if (!Number.isInteger(line.quantity) || line.quantity <= 0) throw new Error(`${line.sku}: quantity must be a positive whole number`);
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
// Sum repeated SKUs so two lines for the same item cannot each pass the check alone.
|
|
42
|
+
const totals = new Map();
|
|
43
|
+
for (const line of lines) totals.set(line.sku, (totals.get(line.sku) ?? 0) + line.quantity);
|
|
44
|
+
const single = eligible.find((location) => [...totals].every(([sku, quantity]) => availableAt(location, sku) >= quantity));
|
|
31
45
|
if (single) {
|
|
32
46
|
const allocations = lines.map((line) => ({ sku: line.sku, location: single.location, quantity: line.quantity }));
|
|
33
47
|
return { allocations, shortfalls: [], locations: [single.location] };
|
|
@@ -48,7 +62,7 @@ export function allocateOrder(order, stock) {
|
|
|
48
62
|
if (free === 0) continue;
|
|
49
63
|
const take = Math.min(free, needed);
|
|
50
64
|
allocations.push({ sku: line.sku, location: location.location, quantity: take });
|
|
51
|
-
levels
|
|
65
|
+
reserve(levels, line.sku, take);
|
|
52
66
|
used.add(location.location);
|
|
53
67
|
needed -= take;
|
|
54
68
|
}
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: "The signature checker requires Node.js 20 or newer. No network access."
|
|
6
6
|
metadata:
|
|
7
7
|
author: Naser Sadrmanesh
|
|
8
|
-
version: "0.3.
|
|
8
|
+
version: "0.3.3"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Order and webhook reliability
|
|
@@ -34,6 +34,8 @@ Do not dedupe only on a delivery attempt id. Those change when the sender times
|
|
|
34
34
|
|
|
35
35
|
Store: event id, topic, resource id, raw body, signature verification time, and processing status (`stored`, `applied`, `ignored`). Ignore a duplicate by returning 2xx and the original result. A second apply is a bug.
|
|
36
36
|
|
|
37
|
+
The most common duplicate is a redelivery after a timeout. The handler stored and applied the event, but the sender timed out before the 2xx arrived and posts the same event again, often with a new delivery id. The retry must find the inbox row already `applied`, return 2xx, and run no side effect: no second fulfillment, no second confirmation email, no second stock decrement. This only holds when each side effect is recorded in the same transaction that marks the row `applied`, or is keyed on the event id where it runs.
|
|
38
|
+
|
|
37
39
|
## Apply rules
|
|
38
40
|
|
|
39
41
|
- Compare amounts in integer minor units before marking an order paid.
|
|
@@ -82,6 +84,7 @@ There is no separate backoff retry of one payload described here. Timeouts and r
|
|
|
82
84
|
|
|
83
85
|
- Parsing JSON and signing `JSON.stringify(object)` will not match. The sender signed the original bytes, including key order and spacing.
|
|
84
86
|
- Returning 200 before the inbox write loses the event if the process dies.
|
|
87
|
+
- A slow handler that applies the event and sends the email before answering invites a timeout, and the redelivery sends the email again unless the email is tied to the `applied` row.
|
|
85
88
|
- Applying events in arrival order without a version can reopen a canceled order.
|
|
86
89
|
- Logging the raw body of a payment webhook can capture data you did not mean to keep. Store the body in the inbox with restricted access, and keep card data out of application logs.
|
|
87
90
|
- A shared webhook secret across shops makes a signature from shop A valid on shop B. Use one secret per subscription.
|
|
@@ -90,6 +93,7 @@ There is no separate backoff retry of one payload described here. Timeouts and r
|
|
|
90
93
|
|
|
91
94
|
- The checker accepts a known body and secret and rejects a one-byte change.
|
|
92
95
|
- A duplicated delivery creates one inbox row and one state change.
|
|
96
|
+
- A redelivery after a sender timeout, where the first attempt was stored and applied but the 2xx never arrived, returns 2xx and repeats no side effect: one fulfillment, one email, one stock decrement.
|
|
93
97
|
- An older event received after a newer one does not downgrade a paid order.
|
|
94
98
|
- A wrong amount does not mark the order paid.
|
|
95
99
|
- Stopping the database makes the handler return a retryable status and, after recovery, the redelivered event still applies once.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Treat the retry after a timeout as a duplicate of an event you already applied.
|
|
2
|
+
|
|
3
|
+
Look up the inbox row by topic, order id, and `date_modified_gmt`. When its status is already `applied`, return 2xx with the original result and run no side effect: no second fulfillment, no second confirmation email, and no second stock decrement. Record the fulfillment, the email, and the stock change in the same transaction that marks the row `applied`, so the first attempt either did all of them once or none of them.
|
|
@@ -24,6 +24,21 @@
|
|
|
24
24
|
"X-WC-Webhook-Delivery-ID"
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "redelivery-after-timeout",
|
|
30
|
+
"task": "The order.paid handler stored and applied the event and created the fulfillment, but WooCommerce timed out before the 2xx arrived and posted the same event again. Make the retry safe.",
|
|
31
|
+
"expects": {
|
|
32
|
+
"must_include": [
|
|
33
|
+
"applied",
|
|
34
|
+
"2xx",
|
|
35
|
+
"side effect",
|
|
36
|
+
"timeout"
|
|
37
|
+
],
|
|
38
|
+
"must_not_include": [
|
|
39
|
+
"X-WC-Webhook-Delivery-ID"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
27
42
|
}
|
|
28
43
|
]
|
|
29
44
|
}
|