Task T3.1: ActorResolver gains findScope (no-create) + assertOwns(principal,
resourceScopeId) → ForbiddenException on mismatch (KG-02). RouteService.approve/
deny now assertOwns on the decision's binding scope before mutating; listDecisions
is scoped to the caller. Fixes the critical cross-tenant mutation (tenant A could
approve tenant B's route). Also adds IiosScope.cellId + IiosOutboundCommand.scopeId
columns (migration tenant-isolation) + cell assignment on scope create. Test:
another tenant approving → Forbidden, decision unchanged, no scope created, empty list.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task S2.2: OutboundService.send gains an optional purpose, passes it to the broker,
and persists the returned consentReceiptRef on IiosOutboundCommand (new nullable
column, migration outbound-consent). RouteService.execute passes 'route_forward';
calendar reminders pass 'meeting_reminder'. CMP-DENY → command BLOCKED, no send.
2 tests: receipt recorded on allow, purpose-DENY blocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reuses support-service bootstrap/health/prisma patterns. Kernel-only Prisma
schema (scope/source_handle/actor/channel/thread/participant/interaction/
message_part/outbox/processed_event) with orgId/appId NOT NULL and
@@unique([scopeId, idempotencyKey]). Health verified: {status:ok,db:true}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>