Br idempotency
Skill Lonsdale201/wp-agent-skills/better-route/br-idempotency
Configure better-route 1.1 replay-cache idempotency with IdempotencyMiddleware and Idempotency-Key. Use for ArrayIdempotencyStore, TransientIdempotencyStore, WpdbIdempotencyStore, installSchema, identity-aware canonical keys, body fingerprints, key conflicts, replay headers, key validation, custom methods/resolvers, or choosing between classic and atomic idempotency. Use AtomicIdempotencyMiddleware instead when concurrent duplicate side effects must be prevented.From its SKILL.md
npx -y skills add Lonsdale201/wp-agent-skills --skill br-idempotencyAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.2 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
better-route: replay-cache idempotency
Use IdempotencyMiddleware when replaying a completed response is useful but simultaneous duplicate execution is acceptable or impossible at another layer. It checks the store, invokes the handler, and stores only after completion; it does not reserve first.
For payments, order creation, subscriptions, account provisioning, or any irreversible side effect, use br-atomic-idempotency instead.
Production setup
use BetterRoute\Middleware\Write\IdempotencyMiddleware;
use BetterRoute\Middleware\Write\WpdbIdempotencyStore;
register_activation_hook(__FILE__, static function (): void {
(new WpdbIdempotencyStore())->installSchema();
});
$idempotency = new IdempotencyMiddleware(
store: new WpdbIdempotencyStore(),
ttlSeconds: 600,
requireKey: true,
);
$router->post('/drafts', $handler)
->protectedByMiddleware('bearerAuth')
->middleware([$auth, $idempotency]);
Store choices:
- Use
WpdbIdempotencyStoreas the durable production default and callinstallSchema(). - Use
TransientIdempotencyStoreonly when transient lifecycle/cache flush semantics are acceptable. - Use
ArrayIdempotencyStoreonly for tests or one-process checks; normal PHP requests do not share its state.
Cross-database wpdb table names containing . are rejected.
1.1 request contract
The middleware applies to POST, PUT, PATCH, and DELETE by default. Override methods if the route needs a different set.
Idempotency-Key must be printable ASCII and no longer than maxKeyLength (default 200). Missing required keys return 400 idempotency_key_required; malformed keys return 400 idempotency_key_invalid.
The default storage key deeply canonicalizes route, authenticated/native WordPress identity, and client key. The default fingerprint deeply canonicalizes route, method, identity, JSON/body params, and request params. Reordered associative keys remain equivalent; ordered lists remain order-sensitive.
On retry:
- same storage key + same fingerprint replays the stored response and adds
Idempotency-Replayed: true; - same storage key + changed fingerprint returns
409 idempotency_conflict; - two concurrent first requests can both execute because classic middleware has no reservation.
Response behavior
WP_REST_Response is normalized to a Better Route response before storage so status, data, and string headers can be replayed safely. Returned WP_Error is not stored. Thrown exceptions are not stored because the middleware never reaches its store call.
A returned Response with a 4xx/5xx status is still a completed return value and can be cached. Decide whether that is appropriate; throw for transient failures or use a custom policy/store if only selected statuses should persist.
For wpdb replay, return arrays/scalars or BetterRoute\Http\Response; arbitrary domain objects are not a supported persisted response contract.
Custom scope
$idempotency = new IdempotencyMiddleware(
store: $store,
requireKey: true,
keyResolver: static fn ($context, string $clientKey): string =>
'tenant:' . current_tenant_id() . ':' . $clientKey,
fingerprintResolver: static fn ($context): string =>
hash('sha256', canonical_domain_payload($context->request)),
);
Custom resolvers own collision resistance and canonicalization. Include every security/business dimension that distinguishes operations; do not concatenate attacker-controlled fields with an ambiguous delimiter.
Review checklist
- Choose atomic middleware when simultaneous duplicates would be unsafe.
- Run auth before idempotency; native WP identity is a fallback, not a replacement for route auth.
- Install the wpdb schema before traffic.
- Choose a TTL that covers real client retry delays.
- Keep keys bounded and generate one key per logical operation.
- Verify identical replay, changed-payload conflict, malformed key, missing key, and two concurrent first requests.
- Plan expired-row cleanup for high-volume wpdb tables; on-access expiry alone does not purge every unused old key.
Related skills
- Use
br-atomic-idempotencyfor reservation-before-handler semantics. - Use
br-routesfor access intent and middleware ordering. - Use
br-error-contractfor idempotency error codes.
References
- Verified source paths:
src/Middleware/Write/IdempotencyMiddleware.phpsrc/Middleware/Write/WpdbIdempotencyStore.phpsrc/Middleware/Write/TransientIdempotencyStore.phpsrc/Middleware/Write/StoredResponseCodec.php
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.