feat(p9): per-tenant outbound quota + cellId assignment

Task T3.4: OutboundService gains a per-tenant egress cap (tenantLimit/tenantWindowMs,
IIOS_TENANT_OUTBOUND_*) counting a scope's commands in the window — a noisy tenant
is RATE_LIMITED while others keep sending (noisy-neighbor protection). Persists
scopeId on every command (fixes the previously-unscoped table). resolveScope assigns
IiosScope.cellId (IIOS_CELL_ID, default cell-default) — the cell-partition hook.
Tests: per-tenant cap isolates tenants; new scopes carry a cellId.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 21:55:14 +05:30
parent d002c8f521
commit e7b8845c8a
3 changed files with 27 additions and 3 deletions
@@ -46,6 +46,7 @@ describe('Tenant isolation (P9 / KG-10 — cross-tenant blast radius)', () => {
it('tenant B cannot read or mutate tenant A resources, and sees none of them', async () => {
// ── Seed tenant A: a route decision, an AI artifact, a meeting ──
const scopeA = await actors.resolveScope(A);
expect(scopeA.cellId).toBeTruthy(); // new scopes carry a cell assignment (P9 cell hook)
const interactionId = await interactionFor(A);
await prisma.iiosRouteBinding.create({
data: { scopeId: scopeA.id, originChannelType: 'WHATSAPP', originRef: 'adult-group', destinationChannelType: 'PORTAL', destinationRef: 'adults', requiresReview: true, enabled: true },