feat(iios): idempotency-command ledger + IdempotencyService (P9)

Adds the doc-mandated IiosIdempotencyCommand table + a reusable
IdempotencyService.run() that writes IN_FLIGHT at command start, replays the
cached response on a same-key/same-body retry, rejects a same-key/different-body
reuse (409), and retries a FAILED command. Tenant-scoped via
@@unique([scopeId, commandName, idempotencyKey]) (KG-02/KG-06).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 02:19:03 +05:30
parent dbf8f814c0
commit b8b7e0d3e8
7 changed files with 231 additions and 1 deletions
@@ -0,0 +1,23 @@
-- CreateTable
CREATE TABLE "IiosIdempotencyCommand" (
"id" TEXT NOT NULL,
"scopeId" TEXT,
"commandName" TEXT NOT NULL,
"idempotencyKey" TEXT NOT NULL,
"schemaVersion" TEXT NOT NULL DEFAULT 'v1',
"actorRefId" TEXT,
"requestHash" TEXT NOT NULL,
"responseHash" TEXT,
"response" JSONB,
"status" TEXT NOT NULL DEFAULT 'IN_FLIGHT',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completedAt" TIMESTAMP(3),
CONSTRAINT "IiosIdempotencyCommand_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "IiosIdempotencyCommand_scopeId_status_idx" ON "IiosIdempotencyCommand"("scopeId", "status");
-- CreateIndex
CREATE UNIQUE INDEX "IiosIdempotencyCommand_scopeId_commandName_idempotencyKey_key" ON "IiosIdempotencyCommand"("scopeId", "commandName", "idempotencyKey");