feat(service): P6.1 route tables (route_binding, route_decision, moderation_flag) + enums + events

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 14:24:53 +05:30
parent b879c9eba2
commit dfd87cdde7
4 changed files with 167 additions and 0 deletions
@@ -0,0 +1,78 @@
-- CreateEnum
CREATE TYPE "IiosRouteMode" AS ENUM ('MANUAL', 'AUTOMATIC', 'HYBRID', 'SIMULATION_ONLY');
-- CreateEnum
CREATE TYPE "IiosOutputFormat" AS ENUM ('FORWARD', 'THREADED', 'DIGEST', 'SUMMARY', 'TRANSCRIPT');
-- CreateEnum
CREATE TYPE "IiosRouteDecisionState" AS ENUM ('ALLOW', 'DENY', 'REVIEW', 'SUPPRESS', 'SIMULATED');
-- CreateTable
CREATE TABLE "IiosRouteBinding" (
"id" TEXT NOT NULL,
"scopeId" TEXT NOT NULL,
"originChannelType" TEXT NOT NULL,
"originRef" TEXT,
"destinationChannelType" TEXT NOT NULL,
"destinationRef" TEXT,
"restrictionProfile" TEXT,
"mode" "IiosRouteMode" NOT NULL DEFAULT 'SIMULATION_ONLY',
"outputFormat" "IiosOutputFormat" NOT NULL DEFAULT 'FORWARD',
"frequency" TEXT NOT NULL DEFAULT 'IMMEDIATE',
"includeAttachments" BOOLEAN NOT NULL DEFAULT false,
"requiresReview" BOOLEAN NOT NULL DEFAULT true,
"rulepackVersion" TEXT NOT NULL DEFAULT 'v1',
"enabled" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "IiosRouteBinding_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "IiosRouteDecision" (
"id" TEXT NOT NULL,
"interactionId" TEXT NOT NULL,
"routeBindingId" TEXT NOT NULL,
"decisionState" "IiosRouteDecisionState" NOT NULL,
"reasonCodes" TEXT[],
"previewPayload" JSONB NOT NULL,
"outputFormat" "IiosOutputFormat" NOT NULL,
"rulepackVersion" TEXT NOT NULL,
"simulationId" TEXT,
"decidedByActorId" TEXT,
"executed" BOOLEAN NOT NULL DEFAULT false,
"executedCommandId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "IiosRouteDecision_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "IiosModerationFlag" (
"id" TEXT NOT NULL,
"interactionId" TEXT NOT NULL,
"flagType" TEXT NOT NULL,
"severity" TEXT NOT NULL DEFAULT 'LOW',
"proposedBy" TEXT NOT NULL DEFAULT 'RULE',
"confidence" DOUBLE PRECISION,
"explanation" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"resolvedAt" TIMESTAMP(3),
CONSTRAINT "IiosModerationFlag_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "IiosRouteBinding_scopeId_originChannelType_idx" ON "IiosRouteBinding"("scopeId", "originChannelType");
-- CreateIndex
CREATE INDEX "IiosRouteDecision_decisionState_idx" ON "IiosRouteDecision"("decisionState");
-- CreateIndex
CREATE UNIQUE INDEX "IiosRouteDecision_interactionId_routeBindingId_key" ON "IiosRouteDecision"("interactionId", "routeBindingId");
-- CreateIndex
CREATE INDEX "IiosModerationFlag_interactionId_idx" ON "IiosModerationFlag"("interactionId");
-- AddForeignKey
ALTER TABLE "IiosRouteDecision" ADD CONSTRAINT "IiosRouteDecision_routeBindingId_fkey" FOREIGN KEY ("routeBindingId") REFERENCES "IiosRouteBinding"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -121,6 +121,29 @@ enum IiosCallbackStatus {
CANCELLED
}
enum IiosRouteMode {
MANUAL
AUTOMATIC
HYBRID
SIMULATION_ONLY
}
enum IiosOutputFormat {
FORWARD
THREADED
DIGEST
SUMMARY
TRANSCRIPT
}
enum IiosRouteDecisionState {
ALLOW
DENY
REVIEW
SUPPRESS
SIMULATED
}
// ─── Kernel tables ────────────────────────────────────────────────
/// Frozen six-vector scope. orgId + appId are REQUIRED (no global-by-null).
@@ -577,3 +600,66 @@ model IiosRateLimitBucket {
@@id([channelType, bucketKey])
}
// ─── P6: Routing (preview-first community forwarding) ──────────────
/// One origin→destination route rule. Preview-first: SIMULATION_ONLY + disabled
/// + requiresReview by default. Restricted destinations are deny-by-default.
model IiosRouteBinding {
id String @id @default(cuid())
scopeId String
originChannelType String
originRef String?
destinationChannelType String
destinationRef String?
restrictionProfile String? // e.g. CHILD / SENIORS / ADULT / PUBLIC
mode IiosRouteMode @default(SIMULATION_ONLY)
outputFormat IiosOutputFormat @default(FORWARD)
frequency String @default("IMMEDIATE")
includeAttachments Boolean @default(false)
requiresReview Boolean @default(true)
rulepackVersion String @default("v1")
enabled Boolean @default(false)
createdAt DateTime @default(now())
decisions IiosRouteDecision[]
@@index([scopeId, originChannelType])
}
/// Per-(interaction, binding) routing decision + rendered preview. No send during simulation.
model IiosRouteDecision {
id String @id @default(cuid())
interactionId String
routeBindingId String
routeBinding IiosRouteBinding @relation(fields: [routeBindingId], references: [id], onDelete: Cascade)
decisionState IiosRouteDecisionState
reasonCodes String[]
previewPayload Json
outputFormat IiosOutputFormat
rulepackVersion String
simulationId String?
decidedByActorId String?
executed Boolean @default(false)
executedCommandId String?
createdAt DateTime @default(now())
@@unique([interactionId, routeBindingId])
@@index([decisionState])
}
/// A deterministic (RULE) or later AI-proposed flag on an interaction. AI can
/// propose but never allow a route (P7).
model IiosModerationFlag {
id String @id @default(cuid())
interactionId String
flagType String
severity String @default("LOW")
proposedBy String @default("RULE")
confidence Float?
explanation String?
createdAt DateTime @default(now())
resolvedAt DateTime?
@@index([interactionId])
}