{"slug":"architecture/integratewise-architecture-v2.0","title":"IntegrateWise Continuity Bridge — Canonical Architecture Specification v2.0","section":"architecture","sectionLabel":"Architecture","status":"superseded","type":"long-form","path":"architecture/integratewise-architecture-v2.0.mdx","bytes":239688,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n> **Canonical Reference:** [CANONICAL_PLATFORM_ARCHITECTURE.md](../architecture/CANONICAL_PLATFORM_ARCHITECTURE.md)  \n> _This document is downstream of the canonical architecture. Refer to the canonical source for current truth._\n\n> **Status:** SUPERSEDED — historical v2 architecture lineage\n> **Date:** 2026-07-03\n> **Product:** One click to make tools and AI work for humans.\n> **Doctrine:** Truth you own. AI you rent. Approval in between.\n> **Moat:** The Continuity Layer — time-accumulated memory, entity graph, and workflow intelligence.\n\n> Current authority is `ARCHITECTURE_LOCK.md`,\n> `IMPLEMENTATION_CONTRACT_v1.1.md` and\n> `CONSOLIDATED_PRODUCT_PLATFORM_CONTRACT_v1.2.md`. This file's old product\n> name, D1 truth, layered UI and provider/runtime claims are not current.\n\n---\n\n## 0. The System in One View\n\n> **\"The system connects tools with one auth, unifies data into a single source of truth, lets users work on that truth in their own workbench, and gives the AI a home where it watches, learns, reasons, proposes, and grows — every day, forever.\"**\n\n### 0.1 The 6-Step Flow\n\nThe user experience is a single continuous loop. Every day the system is smarter. Every day the AI knows more. Every day the user works faster.\n\n```\n┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐\n│ CONNECT │────▶│ HYDRATE │────▶│  WORK   │────▶│   AI    │────▶│ MEMORY  │────▶│ REPEAT  │\n│  (Auth) │     │ (Data)  │     │ (Bench) │     │(Propose)│     │(Compound)│    │(Smarter)│\n└─────────┘     └─────────┘     └─────────┘     └─────────┘     └─────────┘     └─────────┘\n```\n\n| Step                        | What the User Feels                         | What the System Does                                                                                                 |\n| --------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |\n| **1. CONNECT**              | \"One click and Salesforce is here.\"         | Integration Manager handles auth, adapter selection, and capability registration.                                    |\n| **2. HYDRATE**              | \"My data just... appeared.\"                 | Data flows through the Normalizer (8-stage pipeline) into the Spine. Entities resolved. SSOT built. Workbench ready. |\n| **3. WORK**                 | \"One workbench. All tools. One truth.\"      | User works on unified data — no tab switching, no context loss. 8 projections (lenses) adapt to role.                |\n| **4. AI BECOMES PROACTIVE** | \"The AI knew what I needed before I asked.\" | Twin watches the same SSOT. Proposes actions. Helps with daily tasks.                                                |\n| **5. MEMORY**               | \"It remembers everything.\"                  | Human knowledge + AI knowledge live on the Spine. Memory compounds every day.                                        |\n| **6. REPEAT**               | \"Every day it's smarter.\"                   | Triage worker runs nightly. Patterns reinforce. Decisions become permanent.                                          |\n\n### 0.2 The 6-Layer Architecture with OODA Overlaid\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│  LAYER 1 — PRODUCT            │  What the user sees. The Workbench. 8 lenses.  │\n│  (§1)                         │  The Twin's 5 communication channels.            │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 2 — IDENTITY           │  Who. JWT + API Keys. RBAC. Tenant isolation.  │\n│  (§2)                         │  Session management. Revocation.                 │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 3 — INGRESS            │  How. Gateway. Rate limiting. Transport.       │\n│  (§3)                         │  SSE / WebSocket / MCP / Webhooks. Discovery.    │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 4 — CAPABILITY         │  What. Capability Registry. Platform SDK.        │\n│  (§4)                         │  Context Assembly (Entity360). Execution Routing. │\n│                               │  ◄── OODA: OBSERVE + ORIENT (part) ───►       │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 5 — CONTINUITY         │  Context. Adaptive Spine. Memory. Knowledge.   │\n│  (§5)                         │  The Moat. Pipeline is the ONLY writer.          │\n│                               │  ◄── OODA: ORIENT (core) ───►                   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 6 — GOVERNANCE         │  Approval. 0.70/0.85 confidence law. HITL.     │\n│  (§6)                         │  Audit. Compliance.                              │\n│                               │  ◄── OODA: DECIDE ───►                         │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  LAYER 7 — PROVIDER FABRIC    │  Execution. Adapter pattern. Credential wall.  │\n│  (§7)                         │  Circuit breaker. Entity360 injection. T2T.      │\n│                               │  ◄── OODA: ACT ───►                            │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  CROSS-CUTTING: OODA RUNTIME  │  Observe → Orient → Decide → Act → Learn       │\n│  (§8)                         │  Memory compounding closes the loop.              │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  CROSS-CUTTING: ECOSYSTEM     │  Capability fabric. KSP. A2A. SDK variants.      │\n│  (§9)                         │  App composition. Why it multiplies the moat.      │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 0.3 The Spine — 3 Layers\n\nThe Spine is the single source of truth. It is not a database — it is a **living graph** of everything the system knows.\n\n| Layer       | Name        | What Lives Here                             | Storage             | Access Pattern    |\n| ----------- | ----------- | ------------------------------------------- | ------------------- | ----------------- |\n| **Layer 1** | Simple Data | Structured fields, numbers, dates, statuses | D1 (SQLite)         | CRUD via Pipeline |\n| **Layer 2** | Rich Data   | Files, images, documents, conversations     | R2 (object storage) | Blob read/write   |\n| **Layer 3** | Knowledge   | Human + AI knowledge, patterns, decisions   | D1 + Vectorize + KV | Semantic search   |\n\nThe Spine is **provider-agnostic**. A Salesforce Account and a HubSpot Company both resolve to `entity_type = 'organization'` with distinct `provider_id` entries. The SSOC (Stable Single Object Canonical) UUID survives provider swaps.\n\n### 0.4 The Moat Statement\n\n> **The moat is the Continuity Layer.** Not the AI models (you rent those). Not the integrations (those are swappable). The moat is the time-accumulated context: the entity graph, the memory, the governance decisions, the workflow intelligence. Every day the system is smarter because memory compounds. A competitor can copy the code. They cannot copy your accumulated context.\n\n### 0.5 Ecosystem Purpose\n\nThe Ecosystem Architecture (§9) exists because the value of the Bridge is not just what IntegrateWise builds — it is what **others build on it**. The ecosystem enables:\n\n- **Capability Fabric:** A living registry of everything the workspace can do — connected tools, domain packs, shared features, platform primitives.\n- **Knowledge Sharing Protocol (KSP):** How memory, context, and evidence move between agents, tools, and humans without leaking tenant boundaries.\n- **Tool-to-Tool Communication:** Providers communicate through the Bridge, not peer-to-peer — governed, audited, and context-enriched.\n- **Agent-to-Agent Communication:** Autonomous agents coordinate through the Spine as a shared blackboard, mediated by the ambient Twin layer.\n\n---\n\n## 1. The Product Layer (L1 — What the User Sees)\n\n### 1.1 The One-Click Promise\n\n> **\"One click to make tools and AI work for humans.\"**\n\nThe product promise is deceptively simple: a user gives one authentication, and the system handles everything else. The Integration Manager (now a pattern, not a separate service — see §7) discovers the provider's capabilities, establishes the connection, manages consent, registers the provider in the Capability Fabric, and triggers a \"creamy\" (full historical) sync.\n\nThe user feels: _\"I clicked 'Connect Salesforce' and 30 seconds later my data was here.\"_\n\nThe system does: OAuth handshake → credential wall storage → adapter registration → capability catalog update → discovery cache invalidation → sync job enqueue → normalizer pipeline → Spine write → workbench projection refresh.\n\n### 1.2 The Workbench — Where Real Work Happens\n\nThe Workbench is the user's unified workspace. It is not a data viewer — it is a **working surface** where the user acts on unified data without switching contexts.\n\n**Workbench Principles:**\n\n- One workbench. All tools. One truth. No tab switching. No context loss.\n- The workbench reads from the Spine (via `store` and `l2` service bindings)\n- The workbench queries the Capability Fabric to know what actions are available\n- The Twin enriches the workbench with inline insights (see §1.5)\n- Every action is governed (§6) and audited (§5.10)\n\n**Workbench Architecture:**\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                           THE WORKBENCH                                      │\n│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐              │\n│  │  Entity Explorer │  │  Detail Pane    │  │  Action Panel   │              │\n│  │  (Spine query)   │  │  (Entity360)    │  │  (Capability    │              │\n│  │                  │  │                 │  │   Fabric)       │              │\n│  └─────────────────┘  └─────────────────┘  └─────────────────┘              │\n│  ┌─────────────────────────────────────────────────────────────┐           │\n│  │  TWIN ENRICHMENT (inline insights, proposals, context)     │           │\n│  └─────────────────────────────────────────────────────────────┘           │\n│  ┌─────────────────────────────────────────────────────────────┐           │\n│  │  MEMORY PANEL (what the system knows about this entity)    │           │\n│  └─────────────────────────────────────────────────────────────┘           │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 1.3 The 8 Projections (Lenses)\n\nProjections are persona-specific views into the Spine. They are **not** separate data stores — they are filtered, enriched, and formatted queries against the same underlying SSOT.\n\n| Projection           | Audience          | Primary Entities                         | Key Capabilities                                  |\n| -------------------- | ----------------- | ---------------------------------------- | ------------------------------------------------- |\n| **Sales**            | AEs, SDRs         | accounts, contacts, deals, opportunities | forecast, pipeline health, next-best-action       |\n| **Customer Success** | CSMs, TAMs        | accounts, health scores, tickets, NPS    | churn risk, expansion signals, playbooks          |\n| **Finance**          | Finance, RevOps   | invoices, subscriptions, MRR, metrics    | billing reconciliation, revenue recognition       |\n| **Executive**        | C-suite, VPs      | KPIs, trends, forecasts, alerts          | dashboards, anomaly detection, strategic insights |\n| **Architecture**     | Engineers, PMs    | projects, sprints, PRs, docs             | tech debt, delivery velocity, resource allocation |\n| **Operations**       | Ops, Admin        | workflows, connectors, sync status       | data quality, automation health, governance       |\n| **Personal**         | Individual user   | tasks, calendar, reminders, notes        | daily briefing, task suggestions, time management |\n| **Developer**        | Platform builders | API, SDK, webhooks, capabilities         | debugging, testing, extension development         |\n\nEach projection is implemented as a **materialized view** in D1, updated by the Projection Pipeline (§13.7). The `l2` service serves projection queries from KV cache when possible, D1 when stale.\n\n### 1.4 Essential User Work (Non-AI, Deterministic)\n\nNot everything is AI. The Workbench supports essential, deterministic work that humans do every day:\n\n- **Entity CRUD:** Create, read, update, archive entities in the Spine (via `store` → `pipeline`)\n- **Relationship editing:** Link entities (e.g., \"this contact belongs to that account\")\n- **Task management:** Create and manage tasks, assign owners, set due dates\n- **Document attachment:** Upload files to R2, link to entities via Spine relationships\n- **Projection switching:** Change lens (e.g., from Sales to CS) without losing context\n- **Approval workflow:** Review and act on proposals in My Desk (§6.3)\n- **Search:** Full-text search across entities, memory, and knowledge\n\nAll of these are **non-AI** paths that work even when the Twin is offline. They write to the Spine via the Pipeline. They are governed by the same rules. They are audited.\n\n### 1.5 The Twin's 5 Communication Channels\n\nThe Twin communicates with the user through 5 distinct channels. Each channel has a purpose, a tone, and a governance boundary.\n\n| Channel                     | Purpose                                        | Interface                       | Governance                                        |\n| --------------------------- | ---------------------------------------------- | ------------------------------- | ------------------------------------------------- |\n| **1. Proposals**            | \"I think you should do X.\"                     | Approvals Workbench (My Desk)   | ≥0.85 auto-approve; 0.70–0.85 HITL; <0.70 discard |\n| **2. Notifications**        | \"Something happened that matters.\"             | Push, email, Slack              | Always allowed; no governance gate                |\n| **3. Morning Briefing**     | \"Here's what you need to know today.\"          | Daily summary (email / in-app)  | Read-only; no write side effects                  |\n| **4. Workbench Enrichment** | \"Here's context about what you're looking at.\" | Inline insights, Entity360 pane | Read-only; no governance gate                     |\n| **5. Direct Messaging**     | \"Let's talk.\"                                  | Slack / email / in-app chat     | Conversation memory; no auto-actions              |\n\n**The Twin is AMBIENT.** It is not a separate service that users call. It is a reasoning quality that permeates Context Assembly, Memory, and Knowledge. The Twin lives in the `twin-orchestrator` Durable Object (one per `tenant_id + user_id`). It reasons over Spine context, proposes through Governance, and persists state in DO storage.\n\n→ See §4.4 for the Twin runtime architecture.\n→ See §5.6 for how the Twin reads and writes Memory.\n→ See §6.3 for the HITL flow that governs Twin proposals.\n\n### 1.6 Morning Briefing & Daily Rhythm\n\nEvery morning, the Twin generates a personalized briefing for each user. This is a **read-only, no-governance** operation.\n\n**Briefing Content:**\n\n- Overnight changes in the Spine (new entities, updated deals, health score changes)\n- Open proposals awaiting review (in My Desk)\n- Today's scheduled tasks and meetings\n- Memory-reinforced suggestions (\"You usually review this account on Tuesdays\")\n- Proactive insights (\"3 accounts show churn risk this week\")\n\n**Briefing Pipeline:**\n\n```\ncontinuity service (cron trigger) → query active memory for user → assemble Entity360 for recent changes → think service (LLM summarization) → generate briefing → enqueue to notification queue → deliver via configured channel (email/Slack/in-app)\n```\n\nThe Morning Briefing is the **daily compound moment** — the user starts their day with more context than the day before, because memory has grown overnight.\n\n---\n\n## 2. Identity Layer (L2 — Who Are You?)\n\n> _\"Identity gates the door. Every hop carries tenant_id.\"_\n\nThe Identity Layer is the first of two ingress layers. It answers the question: **Who is making this request, and what are they allowed to see?**\n\nThe Gateway is the sole entry point. No internal service accepts direct external traffic. Every request — from a human in the workbench, an AI assistant via MCP, a webhook from Salesforce, or a backend SDK call — passes through the Gateway, is authenticated, scoped to a tenant, and enriched with identity context before any internal service ever sees it.\n\n### 2.1 Authentication (JWT + API Keys)\n\nThe Gateway accepts **exactly two** credential types. All others (Clerk, Stack Auth, Descope, Supabase Auth direct) are rejected at the edge.\n\n| Credential Type | Header Format                      | Use Case                                          | Issuer                                   |\n| --------------- | ---------------------------------- | ------------------------------------------------- | ---------------------------------------- |\n| **Gateway JWT** | `Authorization: Bearer <jwt>`      | Human users, AI assistants, frontend sessions     | `tenants` service (OAuth callback)       |\n| **API Key**     | `Authorization: Bearer iwak_<key>` | Backend SDKs, automation scripts, external agents | `admin` service (provisioned per tenant) |\n\n**Rejected patterns (403):**\n\n- Missing `Authorization` header on protected routes\n- `Basic` auth\n- Provider-issued tokens (Salesforce session, GitHub token) — these belong in the Provider Fabric\n- Expired JWT (returns 401 with `WWW-Authenticate: Bearer error=\"invalid_token\"`)\n- Revoked API key (checked against KV blacklist)\n\n**JWT Specification (RS256, 2048-bit RSA):**\n\n```typescript\nexport interface GatewayJwtPayload {\n  jti: string; // Unique token ID — revocation tracking\n  iss: \"gateway\"; // Issuer\n  sub: string; // user UUID\n  aud: string; // tenant slug or \"integratewise\" for platform admin\n  iat: number; // Issued at\n  exp: number; // Expiration (default 24h, refresh tokens 30d)\n  tenant_id: string; // THE isolation boundary\n  org_id: string; // Organization within tenant\n  role: \"owner\" | \"admin\" | \"member\" | \"viewer\" | \"api\";\n  scope: (\"read\" | \"write\" | \"admin\" | \"webhook\" | \"mcp\")[];\n  sid: string; // Session ID — links to KV session store\n  cid: string; // Correlation ID — propagated across all bindings\n  tier: \"free\" | \"pro\" | \"enterprise\" | \"customer_zero\";\n}\n```\n\n**JWT Lifecycle:**\n\n```\nUser Login (OAuth2) → Identity Provider (Google/GitHub) → tenants Service (OAuth callback)\n                                                                 │\n                                                                 ▼\n                                                        D1: users, orgs, tenants\n                                                                 │\n                                                                 ▼\n                                                        RBAC Resolution → JWT Minting → Session Creation (KV)\n                                                                 │\n                                                                 ▼\n                                                        JWT + Refresh Token → Client Storage\n```\n\n→ See §11.2 for JWT validation flow and security boundaries.\n\n### 2.2 Organizations & Tenant Context\n\nTenant is the **hard isolation boundary**. Org is a soft partition within a tenant.\n\n```\nTenant (tenant_id = \"acme_corp\")\n├── Org: \"Engineering\" (org_id = \"eng_01\")\n│   ├── Users: alice@acme.com (owner), bob@acme.com (member)\n│   └── Resources: scoped to org\n├── Org: \"Sales\" (org_id = \"sales_01\")\n│   ├── Users: carol@acme.com (admin), dave@acme.com (viewer)\n│   └── Resources: scoped to org\n└── Platform-level: tenant owner can see all orgs\n\nTenant (tenant_id = \"startup_xyz\")\n├── Org: \"Default\" (single-org tenant)\n└── ...\n```\n\n**Tenant Resolution is Stratified — never trust client input.**\n\nPriority (highest wins):\n\n1. JWT claim `tenant_id` (for Bearer JWT)\n2. API key lookup → D1 `api_keys` table → `tenant_id`\n3. **NOTHING ELSE** — no `x-tenant-id` header, no query param, no URL path\n\nIf credential type is JWT and `aud` !== `tenant_id`, return 403. If API key tenant is suspended, return 403 with `error: \"tenant_suspended\"`.\n\nAfter resolution, the Gateway injects `TenantContext` into the request's Cloudflare context (`request.cf.integratewise`). Every downstream service binding receives this implicitly. No header injection is allowed.\n\n### 2.3 RBAC & Permissions\n\n**Role Hierarchy:**\n\n```\n                    ┌─────────────┐\n                    │   owner     │  ← Can do everything, including delete tenant\n                    └──────┬──────┘\n                           │\n              ┌────────────┼────────────┐\n              ▼            ▼            ▼\n        ┌─────────┐  ┌─────────┐  ┌─────────┐\n        │  admin  │  │  member │  │  viewer │\n        │(manage  │  │(execute│  │(read    │\n        │ users)  │  │ actions)│  │ only)   │\n        └────┬────┘  └────┬────┘  └─────────┘\n             │            │\n             ▼            ▼\n        ┌─────────┐  ┌─────────┐\n        │ api_key │  │ webhook │\n        │(scoped  │  │(inbound │\n        │ access) │  │ only)   │\n        └─────────┘  └─────────┘\n```\n\n**Permission format:** `<resource>:<action>`\n\nExamples: `spine:read`, `spine:write`, `connector:create`, `twin:propose`, `govern:approve`, `mcp:invoke`, `webhook:receive`.\n\n**RBAC Enforcement at Two Boundaries:**\n\n1. **Gateway (Coarse):** Does this role have `spine:read`? Reject early — 403 before any service binding call.\n2. **Service (Fine-grained):** `WHERE tenant_id = ? AND org_id = ?` on every query. Record-level ownership checks.\n\n```typescript\n  if (role.permissions.includes(required)) return true;\n  const [resource] = required.split(\":\");\n  if (role.permissions.includes(`${resource}:*`)) return true;\n  if (role.permissions.includes(\"*:*\")) return true;\n  if (role.inherits_from) return checkInherited(role.inherits_from, required);\n  return false;\n}\n```\n\n→ See §11.1 for the 4-Zone Trust Model and how RBAC fits into Zero-Trust.\n\n### 2.4 Session Management\n\nSessions are ephemeral state stored in Cloudflare KV with TTL. They are NOT the source of truth — the JWT is. Sessions enable revocation, multi-device tracking, and security events.\n\n```typescript\nexport interface SessionRecord {\n  sid: string;\n  tenant_id: string;\n  org_id: string;\n  user_id: string;\n  jti: string; // links to JWT\n  issued_at: number;\n  expires_at: number;\n  ip_hash: string; // SHA-256 of IP (privacy-preserving)\n  user_agent_hash: string; // SHA-256 of UA string\n  device_fingerprint: string;\n  last_active_at: number;\n  revoked_at: number | null;\n  revoke_reason: string | null;\n}\n```\n\n**KV Keys:**\n\n- `sessions:<sid>` → SessionRecord (TTL = JWT exp)\n- `sessions_by_jti:<jti>` → sid (for revocation lookup)\n- `sessions_by_user:<user_id>` → JSON[sid[]] (for \"log out all devices\")\n\n**Revocation flows:**\n\n1. User \"Log out\" → revoke single session\n2. User \"Log out all devices\" → revoke all sessions for user\n3. Admin \"Suspend user\" → revoke all sessions + blacklist JWT jti\n4. Security event (suspicious IP) → automatic revocation\n\nBlacklist: KV `jwt_blacklist:<jti>` = \"revoked\" (TTL = remaining JWT lifetime). Gateway checks blacklist on EVERY JWT verification.\n\n---\n\n## 3. Ingress Layer (L2 — How Did You Arrive?)\n\n> _\"Every request passes through the Gateway. Every request is validated, rate-limited, and routed. No exceptions.\"_\n\n### 3.1 Gateway — Single Entry Point\n\nThe Gateway (`integratewise-gateway`) is a single Cloudflare Worker. It is the only surface that external consumers ever touch.\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                     GATEWAY REQUEST LIFECYCLE                                │\n│                                                                              │\n│  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────────┐   │\n│  │  WAF    │──▶│  TLS    │──▶│  CORS   │──▶│  AuthN  │──▶│ Rate Limit  │   │\n│  │ (CF)    │   │ (CF)    │   │ (GW)    │   │  (GW)   │   │   (GW)      │   │\n│  └─────────┘   └─────────┘   └─────────┘   └─────────┘   └─────────────┘   │\n│       │            │            │            │                  │           │\n│       ▼            ▼            ▼            ▼                  ▼           │\n│    Blocked     Blocked      Blocked      401/403           429              │\n│    (WAF)       (TLS)        (CORS)       (Auth)         (Rate limit)       │\n│                                                                              │\n│  ┌─────────────┐   ┌─────────────┐   ┌─────────────────────────────────┐   │\n│  │   Tenant    │──▶│   Request   │──▶│         ROUTER                  │   │\n│  │  Resolver   │   │  Validation │   │  ┌─────────┐  ┌─────────────┐  │   │\n│  │             │   │  (Zod)      │   │  │ Route   │  │  Service    │  │   │\n│  └─────────────┘   └─────────────┘   │  │  Match  │──│  Binding    │──┼───┼──▶\n│                                      │  │         │  │    Call     │  │   │\n│                                      │  └─────────┘  └─────────────┘  │   │\n│                                      └─────────────────────────────────┘   │\n│                                                                              │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  AUDIT ENQUEUE (fire-and-forget via PIPELINE service binding)       │    │\n│  │  → spine_audit_log entry with tenant_id, user_id, route, latency    │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n**Key Invariant:** No internal service accepts direct external traffic. The Gateway is the sole north-plane entry point. Every hop carries `tenant_id`. Cross-tenant access is a 403 (fail-loud, not 404).\n\n### 3.2 Transport Layer (HTTP / SSE / WebSocket / MCP / Webhooks)\n\n| Transport     | Protocol           | Use Case                       | Endpoint                                         | Auth                   |\n| ------------- | ------------------ | ------------------------------ | ------------------------------------------------ | ---------------------- |\n| **HTTP/REST** | HTTP/1.1, HTTP/2   | Primary API for SDKs, webhooks | `api.integratewise.ai/api/v1/*`                  | JWT or API Key         |\n| **SSE**       | Server-Sent Events | MCP streams, real-time signals | `mcp.integratewise.ai/v1/mcp`                    | JWT                    |\n| **WebSocket** | WS over TLS        | Real-time workbench (future)   | `ws.integratewise.ai/v1/stream`                  | JWT                    |\n| **MCP**       | stdio / SSE        | AI assistant integration       | `mcp.integratewise.ai/v1/mcp`                    | JWT                    |\n| **Webhooks**  | HTTP POST          | Provider callbacks             | `api.integratewise.ai/api/v1/webhooks/:provider` | Signature verification |\n\n**MCP SSE Transport:**\n\n```typescript\nexport interface McpSseSession {\n  session_id: string;\n  tenant_id: string;\n  user_id: string;\n  created_at: number;\n  last_event_at: number;\n  event_stream: ReadableStream;\n  tools_version: string; // Discovery cache version\n}\n\nexport type McpSseEvent =\n  | { type: \"connected\"; session_id: string; tenant_id: string }\n  | { type: \"tool_catalog\"; tools: McpToolDefinition[] }\n  | { type: \"invocation_result\"; invocation_id: string; result: unknown }\n  | { type: \"signal\"; signal_type: string; payload: unknown }\n  | { type: \"error\"; code: string; message: string }\n  | { type: \"ping\" }; // keep-alive every 30s\n```\n\n**Session lifecycle:**\n\n1. Client POST `/mcp/sessions` → Gateway validates JWT\n2. Gateway creates session in KV (TTL = 1 hour)\n3. Gateway returns SSE stream with initial `connected` event\n4. Client POST `/mcp/tools` → Gateway assembles catalog from Discovery\n5. Client POST `/mcp/invoke` → Gateway routes to MCP_CONNECTOR binding\n6. Result streamed back via SSE\n7. Client DELETE `/mcp/sessions/:id` or TTL expiry → cleanup\n\n**Webhook Transport:**\n\nWebhook ingress is a **separate Worker** (`webhook-ingress`), not the Gateway. This isolates burst traffic from API traffic and allows provider-specific signature verification.\n\n```typescript\nexport interface WebhookEnvelope {\n  webhook_id: string; // idempotency key\n  tenant_id: string;\n  provider: string;\n  event_type: string;\n  received_at: number;\n  payload: unknown;\n  signature_valid: boolean;\n  raw_headers: Record<string, string>;\n}\n```\n\nWebhook processing: Provider POST → verify signature → check idempotency (KV dedup) → extract tenant_id from connection mapping → normalize to WebhookEnvelope → enqueue to NORMALIZER_QUEUE → return 200.\n\n### 3.3 Rate Limiting & Request Validation\n\n**Rate Limit Tiers:**\n\n| Tier          | Request/min | Burst     | Concurrent | MCP Streams | Webhooks  |\n| ------------- | ----------- | --------- | ---------- | ----------- | --------- |\n| free          | 60          | 10        | 5          | 1           | 10/hr     |\n| pro           | 600         | 100       | 25         | 3           | 100/hr    |\n| enterprise    | 6,000       | 500       | 100        | 10          | 1,000/hr  |\n| customer_zero | unlimited   | unlimited | unlimited  | unlimited   | unlimited |\n\nRate limit is enforced at **two levels:**\n\n1. Gateway edge — per-tenant token bucket (CF Rate Limiting API + KV)\n2. Service — per-tenant queue depth (backpressure)\n\n**Request Validation Pipeline (4 Stages):**\n\n```\nStage 1: Structural (Zod schemas)\n  ├── Content-Type header check\n  ├── Body JSON parsing\n  ├── Zod schema validation\n  └── Reject: 400 with zodError.errors array\n\nStage 2: Semantic\n  ├── Tenant exists and is active\n  ├── User exists in tenant\n  ├── Role permits this action\n  └── Reject: 403 with reason code\n\nStage 3: Business Rules\n  ├── Plan limits (e.g., max connectors)\n  ├── Rate limit compliance\n  └── Reject: 402 (payment required) or 429\n\nStage 4: Security\n  ├── Input sanitization (XSS, SQLi patterns)\n  ├── Max body size (1MB for API, 10MB for file upload)\n  └── Reject: 413 or 400\n```\n\n**Critical Zod Rule:** `tenant_id` is NEVER in the request body. It comes from JWT/API key resolution only.\n\n### 3.4 Discovery Subsystem\n\nDiscovery is the **capability handshake**. Every consumer — human, AI agent, SDK — calls `GET /api/v1/discovery` to learn what exists and what they can do. This is a **living contract** that changes as connectors are added, roles are modified, or plan limits are adjusted.\n\n```typescript\nexport interface DiscoveryResponse {\n  identity: { user_id; email; name; tenant_id; tenant_name; org_id; org_name; role; tier };\n  workspace: { connected_connectors; plan_limits; features_enabled; onboarding_complete };\n  projections: ProjectionInfo[]; // 8 lenses, filtered by role\n  capabilities: CapabilityInfo[]; // Named capabilities, filtered by plan\n  features: FeatureInfo[]; // Gated features\n  navigation: NavigationNode[]; // Role-based navigation\n  personalization: { recent_entities; preferred_projection; pinned_capabilities; theme };\n  limits: { rate_limit; storage_used; storage_limit; api_calls };\n  mcp: { endpoint; version; transport; authenticated };\n  version: { api; schema; discovery_cache_version };\n}\n```\n\n**Discovery Assembly Flow (inside Gateway):**\n\n1. Check KV cache: `discovery:<tenant_id>:<role>:<version>` → Cache hit? Return (60s TTL)\n2. Parallel service binding calls: TENANTS → CONNECTOR → AGENT_REGISTRY → L2 → BILLING\n3. Assemble DiscoveryResponse\n4. Write to KV cache (5 min TTL)\n5. Return to client\n\n### 3.5 MCP Pool Architecture (Inbound + Outbound)\n\nThe MCP Pool is split into two halves:\n\n**Inbound MCP Pool (`mcp-connector` service):** AI assistants (Claude, ChatGPT) connect to the Bridge via MCP. They discover tools, invoke capabilities, and receive results. The Gateway handles JWT auth, session management, and SSE streaming. The `mcp-connector` service translates MCP tool calls into Capability Invocations and routes them through Hermes.\n\n**Outbound MCP Pool (`act` service adapters):** When the Bridge needs to call an MCP-native provider (GitHub, Linear, Figma), the `mcp-outbound` adapter in the `act` service establishes an SSE connection to the provider's MCP server, invokes the tool, and normalizes the result.\n\n→ See §7.1 for the adapter taxonomy and how MCP fits into the Provider Fabric.\n→ See §4.5 for the MCP Runtime in the Capability Layer.\n\n---\n\n## 4. Capability Layer (L3 — What Do You Want To Do?)\n\n> _\"The Capability Layer turns intent into execution. It does not own data. It does not own memory. It owns the contract between what the user wants and what the system can do.\"_\n\nThe Capability Layer is the **OODA Execution Fabric**. It does not store context (that is the Continuity Layer), it does not govern (that is the Governance Layer), and it does not touch providers directly (that is the Provider Fabric via adapter modules). The Capability Layer **orchestrates** — it routes, assembles, reasons, and dispatches.\n\n### 4.1 Capability Registry & Fabric\n\nThe Capability Registry is the **single source of truth** for every named action the platform can perform. It is a living catalog — not a static file. Capabilities are registered at deploy time, discovered at runtime, and validated on every invocation.\n\n**Capability URI Format:**\n\n```\ncapability := <namespace> \".\" <name>\nnamespace  := \"platform\" | \"twin\" | <provider> | \"ecosystem\"\nprovider   := \"salesforce\" | \"hubspot\" | \"slack\" | \"github\" | \"stripe\" | ...\nname       := <resource> \".\" <action>\n```\n\nExamples:\n\n- `salesforce.opportunity.update` — Provider capability, Act phase\n- `twin.propose` — Twin reasoning capability, Decide phase\n- `platform.discovery` — Platform capability, Observe phase\n- `ecosystem.broadcast` — Cross-tenant knowledge sharing, Cross-phase\n\n**Registry Schema (D1 + KV Cache):**\n\n```typescript\n  id: text(\"id\").primaryKey(), // capability URI\n  tenantId: text(\"tenant_id\").notNull(),\n  namespace: text(\"namespace\").notNull(),\n  name: text(\"name\").notNull(),\n  displayName: text(\"display_name\"),\n  description: text(\"description\"),\n  version: text(\"version\").notNull().default(\"1.0.0\"),\n  oodaPhase: text(\"ooda_phase\", {\n    enum: [\"observe\", \"orient\", \"decide\", \"act\", \"cross-phase\"],\n  }).notNull(),\n  runtime: text(\"runtime\", { enum: [\"agent\", \"pipeline\", \"workflow\", \"mcp\", \"direct\"] }).notNull(),\n  serviceBinding: text(\"service_binding\").notNull(),\n  handler: text(\"handler\").notNull(),\n  inputSchema: text(\"input_schema\"), // JSON Schema URI\n  outputSchema: text(\"output_schema\"), // JSON Schema URI\n  requiresApproval: integer(\"requires_approval\", { mode: \"boolean\" }).default(false),\n  minConfidence: real(\"min_confidence\").default(0.7),\n  autoApproveThreshold: real(\"auto_approve_threshold\").default(0.85),\n  requiredRoles: text(\"required_roles\"), // JSON array\n  requiredScopes: text(\"required_scopes\"), // JSON array\n  isActive: integer(\"is_active\", { mode: \"boolean\" }).default(true),\n  isDeprecated: integer(\"is_deprecated\", { mode: \"boolean\" }).default(false),\n  deprecationNote: text(\"deprecation_note\"),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  updatedAt: integer(\"updated_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n});\n```\n\n**Registry Operations:**\n\n- `register()` — called by service deploy hooks; writes to D1, invalidates KV cache\n- `resolve(capabilityUri, tenantId)` — hot path; reads KV cache first, D1 fallback\n- `list(filters)` — powers Discovery endpoint\n- `validate(capabilityUri, tenantId, callerRole, payload)` — schema + permission validation\n- `deprecate()` — marks capability as deprecated, schedules removal\n\n**Registry Caching:**\n| Tier | TTL | Source | Invalidation |\n|------|-----|--------|-------------|\n| KV Cache | 5 min | `agent-registry` writes to KV on registration | Service deploy, manual update |\n| D1 | Persistent | Source of truth | Never directly by runtime |\n| Service Binding Memo | 30 sec | Gateway memoizes binding resolution | TTL expiry |\n\n### 4.2 Platform SDK & Contracts\n\nThe Platform SDK (`packages/sdk/`) is the **only** consumer-facing code artifact. It provides typed wrappers around every capability. Consumers import `@integratewise/sdk` — they never import internal service topology.\n\n**Five SDK Variants** (detailed in §9.4):\n\n| Package            | Size    | Use Case                                        |\n| ------------------ | ------- | ----------------------------------------------- |\n| `@iw/sdk-full`     | ~180 KB | Full workbench builders, extension developers   |\n| `@iw/sdk-lite`     | ~85 KB  | Browser/Node apps, dashboards, CLI tools        |\n| `@iw/sdk-content`  | ~45 KB  | Mobile apps, embeddable widgets (read-only)     |\n| `@iw/sdk-minimal`  | ~12 KB  | Telemetry + events only (analytics, logging)    |\n| `@iw/sdk-headless` | ~3 KB   | Server-side, edge functions, background workers |\n\n**Capability Invocation Envelope (wire format):**\n\n```typescript\nexport interface CapabilityInvocation {\n  capability: string; // Capability URI\n  tenantId: string; // Extracted from JWT claim — NEVER from client payload\n  userId: string; // Extracted from JWT sub claim\n  role: string; // From JWT rbac claim\n  correlationId: string; // Distributed tracing\n  scopes: string[]; // JWT scope array\n  payload: unknown; // Validated against capability.inputSchema\n  contextHints?: ContextHint[]; // Passed to intelligence service\n  oodaPhase: OodaPhase; // Routing and telemetry\n}\n\nexport interface CapabilityResponse<T = unknown> {\n  success: boolean;\n  data?: T;\n  error?: CapabilityError;\n  confidence?: number; // Populated by Decide-phase capabilities\n  governance?: GovernanceState; // For capabilities requiring approval\n  meta: ResponseMeta; // Telemetry\n}\n```\n\n### 4.3 Context Assembly (Entity360)\n\nContext Assembly is the **Orient phase engine**. It gathers Entity360, Memory, Knowledge, and Evidence into a structured context window for reasoning and execution. It runs in the `intelligence` service.\n\n**Assembly Pipeline (4 Stages):**\n\n```\nINPUT: CapabilityInvocation + ContextHints\n       │\n       ▼\n┌─────────────┐   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐\n│  Stage 1    │──▶│  Stage 2    │──▶│  Stage 3    │──▶│  Stage 4    │\n│  RESOLVE    │   │   FETCH     │   │  ASSEMBLE   │   │  OPTIMIZE   │\n│             │   │             │   │             │   │             │\n│ • Entity    │   │ • Spine     │   │ • Merge     │   │ • Truncate  │\n│   resolution│   │   entities  │   │   sources   │   │   to window │\n│ • SSOC bind │   │ • Memory    │   │ • Rank by   │   │ • Prioritize│\n│ • Type infer│   │ • Knowledge │   │   relevance │   │ • Deduplicate│\n└─────────────┘   │ • Evidence  │   │ • Inject    │   │ • Compress  │\n                  └─────────────┘   │   metadata  │   └─────────────┘\n                                    └─────────────┘\n\nOUTPUT: AssembledContext (Entity360 + Memory + Knowledge + Tools)\n```\n\n**AssembledContext Output:**\n\n```typescript\nexport interface AssembledContext {\n  entity360: Entity360; // The entity at the center\n  memory: MemoryEntry[]; // Active tier, ranked by recency + relevance\n  knowledge: KnowledgeHit[]; // RAG results from Vectorize + AI Search\n  tools: ToolCatalog; // Available capabilities for this tenant + role\n  evidence: Evidence[]; // Audit trail, past decisions, lineage\n  relationships: Relationship[]; // Connected entities within 2 hops\n  prompt: StructuredPrompt; // Ready for LLM consumption\n  assemblyMeta: AssemblyMeta; // Process metadata\n}\n```\n\n→ See §5.7 for the Knowledge RAG pipeline.\n→ See §5.6 for Memory query and retrieval.\n→ See §10.2 for Memory schema definitions.\n\n### 4.4 ADK Runtime & Twin (Ambient)\n\nThe Twin is **not a separate service that consumers call**. It is a reasoning quality that permeates the Capability Layer. The Twin lives inside the `twin-orchestrator` Durable Object — one per `tenant_id + user_id`.\n\n**Twin Posture: AMBIENT**\n\n- The Twin observes the Spine continuously (via DO alarm every 5 minutes)\n- It proposes actions through the Governance layer (§6)\n- It enriches the workbench with inline insights (§1.5)\n- It generates the Morning Briefing (§1.6)\n- It participates in Agent-to-Agent communication (§9.3)\n\n**Twin Durable Object:**\n\n```typescript\nexport class TwinOrchestrator implements DurableObject {\n  private contextWindow: ContextWindow | null = null;\n  private lastActivity: number = Date.now();\n\n  constructor(state: DurableObjectState) {\n    // Restore from DO storage on activation\n    state.blockConcurrencyWhile(async () => {\n      this.contextWindow = await state.storage.get<ContextWindow>(\"context\");\n    });\n  }\n\n  async fetch(request: Request): Promise<Response> {\n    this.lastActivity = Date.now();\n    // SECURITY: Validate DO ID matches tenant_id + user_id\n    const { tenantId, userId, action, payload } = await request.json();\n    const expectedId = this.state.id.toString();\n    const derivedId = this.state.idFromName(`${tenantId}:${userId}`).toString();\n    if (expectedId !== derivedId) return new Response(\"TENANT_MISMATCH\", { status: 403 });\n\n    switch (action) {\n      case \"reason\":\n        return this.handleReason(payload);\n      case \"propose\":\n        return this.handlePropose(payload);\n      case \"context_snapshot\":\n        return this.handleSnapshot();\n      default:\n        return new Response(\"UNKNOWN_ACTION\", { status: 400 });\n    }\n  }\n\n  async alarm(): Promise<void> {\n    // Every 5 minutes: decay context, snapshot to D1\n    if (this.contextWindow) {\n      const decayed = await applyDecay(this.contextWindow);\n      await this.state.storage.put(\"context\", decayed);\n      await backupToD1(this.state.id.toString(), decayed);\n    }\n    // Hibernation check: idle > 10 minutes → allow hibernation\n    if (Date.now() - this.lastActivity > 10 * 60 * 1000) {\n      await this.state.storage.deleteAlarm();\n    }\n  }\n}\n```\n\n**DO ID Policy:** `idFromName(tenant_id + \":\" + user_id)` — NEVER `idFromName(\"global\")`. This is a P0 security requirement. → See §11.2 (P0-4).\n\n→ See §8.2 for the Twin's role in the OODA loop.\n→ See §5.6 for how the Twin reads and writes Memory.\n\n### 4.5 MCP Runtime\n\nThe MCP Runtime bridges the Bridge's capability model with the Model Context Protocol (MCP). MCP is both an inbound consumer (AI assistants discover and invoke Bridge capabilities) and an outbound provider (the Bridge discovers and invokes external MCP-native tools).\n\n**Inbound MCP:**\n\n- `mcp-connector` service handles SSE sessions, tool catalog assembly, and invocation routing\n- Tool catalog is assembled from the Capability Registry, filtered by tenant's connected providers and user's RBAC role\n- Every MCP invocation is translated to a Capability Invocation and routed through Hermes\n- Read-only default for MCP; write requires `scope:write` + governance\n\n**Outbound MCP:**\n\n- `mcp-outbound` adapter in the `act` service connects to provider MCP servers (GitHub, Linear, Figma)\n- Entity360 is injected into MCP tool calls as `_iw_context` parameter\n- Tool whitelist per tenant restricts which MCP tools can be called\n\n→ See §3.2 for MCP transport specification.\n→ See §7.1 for the MCP adapter in the Provider Fabric.\n→ See §11.6 for MCP security boundaries (P0-9, P1-4).\n\n### 4.6 Execution Routing\n\nThe Capability Router (Hermes Durable Object) is the central message queue + execution router. It maintains stateful routing tables, in-flight execution tracking, and backpressure management.\n\n**Routing Flow:**\n\n```\nGateway → CapabilityRouter (hermes DO) → RegistryCache (KV + D1)\n                                              │\n              ┌───────────────┼───────────────┤\n              ▼               ▼               ▼\n        ┌──────────┐    ┌──────────┐    ┌──────────┐\n        │  Direct  │    │  Agent   │    │ Workflow │\n        │ Handler  │    │ Runtime  │    │ Engine   │\n        │ (act)    │    │(iw-agent  │    │(workflow │\n        │          │    │ runtime) │    │ DO)      │\n        └──────────┘    └──────────┘    └──────────┘\n              │               │               │\n              └───────────────┼───────────────┘\n                              ▼\n                    ┌──────────────────┐\n                    │  Adapter Layer   │  ◄── Provider Fabric (Layer 6)\n                    │  (act adapters)  │\n                    └──────────────────┘\n                              │\n                              ▼\n                    ┌──────────────────┐\n                    │  PIPELINE_QUEUE  │  ──▶ Spine Writeback (Layer 4)\n                    └──────────────────┘\n```\n\n**Hermes DO Route Method:**\n\n1. RESOLVE — capability URI → runtime config (from Registry)\n2. VALIDATE — schema + permissions\n3. GOVERNANCE PRE-CHECK — block before expensive operations if `requiresApproval`\n4. CONTEXT ASSEMBLY — for Orient+ phases, assemble Entity360 + Memory + Knowledge\n5. EXECUTE — route to appropriate runtime (direct / agent / pipeline / workflow / mcp)\n6. POST-EXECUTION GOVERNANCE — for Decide phase, evaluate confidence and queue if needed\n7. TELEMETRY — record duration, success, OODA phase, runtime type\n\n### 4.7 Agent vs Pipeline Boundaries\n\nThis is a **critical conceptual boundary** that determines governance, audit, and approval requirements.\n\n| Dimension            | Pipeline                            | Agent / Agentic                       |\n| -------------------- | ----------------------------------- | ------------------------------------- |\n| **Reads from Spine** | Yes                                 | Yes                                   |\n| **Writes to Spine**  | Yes (the ONLY writer)               | Only via Pipeline enqueue             |\n| **Calls tools**      | No                                  | Yes (via act adapters)                |\n| **Side effects**     | Deterministic (sync, normalization) | Non-deterministic (reasoning, LLM)    |\n| **Governance**       | Rule-based (schema validation)      | Confidence-scored (0.70/0.85 law)     |\n| **Approval**         | None (deterministic)                | HITL for 0.70–0.85                    |\n| **Audit**            | Every step logged                   | Every proposal + execution logged     |\n| **Examples**         | Normalizer, sync, projection        | Twin propose, AI enrichment, workflow |\n\n**Gray Area: Enrichment**\n\n- LLM reads from Spine + writes structured data to Spine (e.g., \"summarize this account's last 10 emails\")\n- No tool calls, no side effects outside Spine\n- Auto-approved (no governance gate) because output is deterministic given input\n- Still logged to `spine_audit_log` with `actor_type: 'twin'` and `via: 'enrichment'`\n\n→ See §6.5 for the full Agentic vs Prompt vs Enrichment classification.\n\n---\n\n## 5. Continuity Layer (L4 — What Do We Know?)\n\n> _\"The Continuity Layer is the moat of the IntegrateWise Continuity Bridge. It is the only place where context is written, compounded, and governed. No consumer, agent, or provider touches the Spine directly.\"_\n\nThe Continuity Layer is Layer 4 of the 6-layer architecture. It sits between the Capability Layer (north) and the Governance Layer (south). Every inbound signal flows through the Normalizer. Every outbound action is gated by Governance. Every memory decays on a schedule. Every audit log is immutable.\n\n### 5.1 Adaptive Spine (SSOT)\n\nThe Adaptive Spine is the **single source of context**. It is a directed property graph stored in D1, accessed only through the `pipeline` service (the sole writer), and read by `intelligence`, `knowledge`, `continuity`, `govern`, and `twin-orchestrator`.\n\n**Spine Schema (D1):**\n\n```typescript\n  id: text(\"id\").primaryKey(), // SSOC UUID (v4, stable)\n  tenantId: text(\"tenant_id\").notNull(),\n  entityType: text(\"entity_type\").notNull(), // 'account', 'contact', 'deal', 'task', ...\n  sourceId: text(\"source_id\").notNull(), // Provider-native ID\n  sourceProvider: text(\"source_provider\").notNull(), // 'salesforce', 'hubspot', 'slack'\n  canonicalName: text(\"canonical_name\"),\n  traits: text(\"traits\", { mode: \"json\" }), // Normalized key-value traits\n  ssocConfidence: real(\"ssoc_confidence\"), // 0.0–1.0\n  firstSeenAt: integer(\"first_seen_at\", { mode: \"timestamp\" }),\n  lastModifiedAt: integer(\"last_modified_at\", { mode: \"timestamp\" }),\n  archivedAt: integer(\"archived_at\", { mode: \"timestamp\" }),\n  // Tenant isolation: every query MUST include WHERE tenant_id = ?\n});\n\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  sourceEntityId: text(\"source_entity_id\")\n    .notNull()\n    .references(() => entities.id),\n  targetEntityId: text(\"target_entity_id\")\n    .notNull()\n    .references(() => entities.id),\n  relationType: text(\"relation_type\").notNull(), // 'owns', 'reports_to', 'participates_in'\n  confidence: real(\"confidence\").notNull(), // 0.0–1.0\n  provenance: text(\"provenance\"), // 'inferred', 'declared', 'synced'\n  firstInferredAt: integer(\"first_inferred_at\", { mode: \"timestamp\" }),\n});\n\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  entityId: text(\"entity_id\"),\n  action: text(\"action\").notNull(), // 'create', 'update', 'delete', 'read', 'mcp_invoke'\n  actor: text(\"actor\").notNull(), // 'user:<id>', 'twin', 'agent:<id>', 'system'\n  via: text(\"via\"), // 'mcp', 'webhook', 'sync', 'act', 'govern'\n  payloadHash: text(\"payload_hash\"), // SHA-256 of normalized payload\n  timestamp: integer(\"timestamp\", { mode: \"timestamp\" }).notNull(),\n  // Index: (tenant_id, timestamp) for audit queries\n});\n```\n\n**SSOC (Stable Source-Object Correlation):**\n\nEvery entity carries a **stable UUID** (SSOC) that survives provider swaps. When Salesforce is swapped for HubSpot, the SSOC remains. The `sourceId` and `sourceProvider` columns change; `id` does not.\n\n```\n┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐\n│  Salesforce ID  │────▶│   SSOC UUID     │◄────│   HubSpot ID    │\n│  0015g00000Lx   │     │  (stable)       │     │  987654321      │\n└─────────────────┘     └─────────────────┘     └─────────────────┘\n        │                        │                       │\n        └─────── same entity ────┴──────── same entity ──┘\n```\n\n**Spine Access Rules:**\n\n| Operation  | Allowed Service                                                                         | Queue / Binding  | Constraint                             |\n| ---------- | --------------------------------------------------------------------------------------- | ---------------- | -------------------------------------- |\n| **WRITE**  | `pipeline` only                                                                         | `PIPELINE_QUEUE` | Must carry `tenant_id` from JWT        |\n| **READ**   | `intelligence`, `knowledge`, `continuity`, `govern`, `twin-orchestrator`, `l2`, `store` | Service bindings | `WHERE tenant_id = ?` enforced         |\n| **AUDIT**  | All services                                                                            | Direct D1 write  | `spine_audit_log` append-only          |\n| **DELETE** | `pipeline` only (soft delete)                                                           | `PIPELINE_QUEUE` | Sets `archivedAt`; never `DELETE FROM` |\n\n**Invariant:** `pipeline` is the only writer. If any other service attempts `INSERT` or `UPDATE` on `entities` or `relationships`, the `db-gate` adapter rejects it.\n\n→ See §10.1 for full Spine schema definitions.\n→ See §10.5 for the Entity Relationship Diagram.\n→ See §10.6 for D1 Drizzle ORM definitions.\n\n### 5.2 Connectors, Loader & Sync\n\nThis subdomain handles **all inbound data**. No provider writes directly to the Spine. Every inbound signal — webhook, sync poll, file drop, MCP tool result — flows through the Connector → Loader → Sync → Normalizer → Pipeline → Spine chain.\n\n**Connector Sync Service:**\n\n```typescript\nexport interface SyncJob {\n  id: string;\n  tenantId: string;\n  connectorId: string;\n  provider: string;\n  syncType: \"full\" | \"incremental\" | \"webhook\" | \"creamy\";\n  since: Date;\n  triggeredBy: \"cron\" | \"webhook\" | \"manual\" | \"loader\";\n  status: \"queued\" | \"running\" | \"completed\" | \"failed\" | \"dlq\";\n  recordsFetched: number;\n  recordsNormalized: number;\n  recordsRejected: number;\n}\n\nexport interface SyncAdapter {\n  fetch(credentials: Credentials, config: ConnectorConfig, since: Date): Promise<RawRecord[]>;\n  getSchema(resource: string): Promise<ProviderSchema>;\n  healthCheck(credentials: Credentials): Promise<HealthStatus>;\n}\n```\n\n**The Loader (Cold-Start & Webhook Trigger):**\n\nThe `loader` service is the **on-ramp** for new tenants and real-time events.\n\n```typescript\nexport interface LoaderEvent {\n  type: \"auth.created\" | \"auth.updated\" | \"webhook.received\" | \"nango.sync\";\n  tenantId: string;\n  provider: string;\n  payload: unknown;\n  signature?: string;\n  idempotencyKey: string;\n}\n```\n\n- On `auth.created`: trigger \"creamy\" (full) sync immediately\n- On `webhook.received`: trigger incremental sync from last sync timestamp\n- Signature verification (P0 security) — invalid signatures are rejected and logged\n- Deduplication via idempotency key (KV, 24h TTL)\n\n**Queues involved:**\n\n| Queue                  | Producer                  | Consumer         | Message Type           | Delivery                   |\n| ---------------------- | ------------------------- | ---------------- | ---------------------- | -------------------------- |\n| `CONNECTOR_SYNC_QUEUE` | `loader`, cron, manual UI | `connector-sync` | `SyncJob`              | At-least-once              |\n| `NORMALIZER_QUEUE`     | `connector-sync`          | `normalizer`     | `NormalizationBatch`   | At-least-once              |\n| `PIPELINE_QUEUE`       | `normalizer`              | `pipeline`       | `CanonicalEntityBatch` | Exactly-once (idempotency) |\n\n### 5.3 Normalizer (8-Stage Pipeline)\n\nThe Normalizer transforms **raw provider records** into **canonical Spine entities**. It is the only path from inbound data to the Spine. Every record that enters the Spine has been through all 8 stages.\n\n```\nRaw Record ──► Stage 0 ──► Stage 1 ──► Stage 2 ──► Stage 3 ──► Stage 4\n               (NA0)       (NA1)       (NA2)       (NA3)       (NA4)\n               Parse       Trait       Resolve     Resolve     Link\n                           Detect      Type        Entity      Relations\n\nStage 4 ──► Stage 5 ──► Stage 6 ──► Stage 7 ──► PIPELINE_QUEUE\n(NA4)       (NA5)       (NA6)       (NA7)\nEnrich      Validate    Emit        DLQ / Retry\n& Map       Canonical   Canonical\n```\n\n| Stage   | Name           | Function                                                         | Failure Behavior                              |\n| ------- | -------------- | ---------------------------------------------------------------- | --------------------------------------------- |\n| **NA0** | Parse          | JSON/CSV/XML → structured object                                 | DLQ + audit log                               |\n| **NA1** | Trait Detect   | Identify entity type from field heuristics                       | Retry ×3 → DLQ                                |\n| **NA2** | Resolve Type   | Map to canonical entity type from Schema Provider                | Retry ×3 → DLQ                                |\n| **NA3** | Resolve Entity | SSOC lookup or mint new stable UUID                              | Retry ×3 → DLQ                                |\n| **NA4** | Link Relations | Traverse foreign keys, infer relationships                       | Log low-confidence, continue                  |\n| **NA5** | Enrich & Map   | Apply tenant-specific field mappings, enrich from Knowledge      | Continue (enrichment optional)                |\n| **NA6** | Validate       | Schema validation against Schema Provider contract               | Reject + audit (do NOT DLQ schema violations) |\n| **NA7** | Emit Canonical | Serialize to `CanonicalEntityBatch`, enqueue to `PIPELINE_QUEUE` | Retry ×5 → DLQ (critical path)                |\n\n**Performance SLO:** Normalizer p99 latency < 2s per batch of ≤100 records.\n\n**DLQ & Retry Policy:**\n\n- Max retries: 5\n- Backoff: exponential (1s, 2s, 4s, 8s, 16s)\n- Retryable errors: `NETWORK_TIMEOUT`, `RATE_LIMITED`, `SCHEMA_PROVIDER_UNAVAILABLE`\n- Non-retryable errors: `SCHEMA_VIOLATION`, `CROSS_TENANT_RECORD`, `INVALID_TRAIT`\n- DLQ destination: R2 cold storage (`dlq/{tenantId}/{batchId}.json`) + audit log\n- Cross-tenant detection: `CROSS_TENANT_RECORD` → alert security (P0)\n\n### 5.4 Schema Provider\n\nThe Schema Provider is a **contractual layer** between the proprietary IntegrateWise mechanism and the tenant-owned context. It defines what entities exist, what fields they carry, and how they map to provider schemas — without exposing the mechanism itself.\n\n**Schema Architecture:**\n\n```\n┌─────────────────┐\n│  Schema AI      │  (think service, tenant onboarding)\n│  (think service)│  Hydrates initial schema for new tenants\n└────────┬────────┘\n         │ generates\n         ▼\n┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐\n│  Base Schema    │────►│  Tenant Overlay │────►│  Provider Map   │\n│  (IW canonical) │     │  (custom fields)│     │  (SF/HS/Slack)  │\n└─────────────────┘     └─────────────────┘     └─────────────────┘\n       │                         │                       │\n       └─────────────┬───────────┴───────────────┘\n                     ▼                           ▼\n            ┌─────────────────┐         ┌─────────────────┐\n            │  Schema Store   │         │  Schema Store   │\n            │  (D1)           │         │  (KV cache)     │\n            └─────────────────┘         └─────────────────┘\n```\n\n**Ownership Split:**\n\n- Base Schema = IntegrateWise IP (proprietary, not exposed)\n- Tenant Overlay = Customer context (data sovereignty)\n- Provider Map = Adapter configuration (swappable)\n\n```typescript\nexport interface SchemaProviderAPI {\n  getTenantSchema(tenantId: string): Promise<MergedSchema>; // Base + Tenant Overlay\n  getProviderMap(tenantId: string, provider: string): Promise<ProviderMap>;\n  validate(tenantId: string, entityType: string, payload: unknown): Promise<ValidationResult>;\n  generateInitialSchema(\n    tenantId: string,\n    industry: string,\n    connectedProviders: string[]\n  ): Promise<BaseSchema>;\n}\n```\n\n### 5.5 Workspace & Tenant Scope\n\nThe Workspace is the **tenant-scoped container** for all context. It is not a data structure per se; it is a logical boundary enforced by `tenant_id` on every query, every queue message, and every Durable Object ID.\n\n**Hard Rules:**\n\n1. No cross-tenant query ever succeeds.\n2. Every Durable Object ID includes `tenant_id`.\n3. Every queue message carries `tenant_id` in the envelope.\n4. Every JWT claim includes `tenant_id`.\n5. Service bindings propagate `tenant_id` implicitly.\n\n**Workspace Initialization (First Auth Flow):**\n\n1. Generate initial schema via Schema AI (think service)\n2. Create RBAC roles (owner, tam, account_success, admin, member, viewer)\n3. Initialize memory tiers (continuity service)\n4. Initialize Knowledge (empty RAG corpus)\n5. Set default governance rules\n6. Audit log: `workspace_initialized`\n\n→ See §10.3 for Tenant schema definitions.\n→ See §11.1 for the 4-Zone Trust Model.\n\n### 5.6 Memory — 4 Scopes × 3 Tiers\n\nMemory is **compounding context**. Every agent turn, every user action, every provider writeback enriches memory. Memory decays if not reinforced. Memory is scoped.\n\n**Memory Scopes (v2.0 FINAL):**\n\n| Scope            | Owner          | Lifetime                     | Access                           |\n| ---------------- | -------------- | ---------------------------- | -------------------------------- |\n| **Personal**     | Human user     | Session → Active → Archive   | User + their Twin                |\n| **Work**         | Task / Project | Duration of task             | Project members + Twin           |\n| **Organization** | Tenant         | Permanent (with decay)       | All org members (RBAC-gated)     |\n| **AI**           | Twin / Agent   | Ephemeral → Active → Archive | Twin only (reasoning scratchpad) |\n\n_Audit is cross-cutting Compliance, not a memory scope._\n\n**Memory Tiers (User's Triage Model):**\n\n```\n   Intake ──► Classification ──► Validation ──► Promotion ──► Store\n      │            │                │              │            │\n      │            ▼                │              │            ▼\n      │    Confidence Score         │              │      ┌──────────┐\n      │    (0.0 – 1.0)              │              │      │  HOT     │\n      │                             │              │      │(Active)  │\n      │            ┌────────────────┘              │      │ D1+KV+  │\n      │            ▼                                │      │ Vectorize│\n      │    ┌───────────────┐    ≥0.85 ────────────┘      └──────────┘\n      └───►│   TRIAGE      │◄───────────────────────────────│\n           │  (triage      │                                 │ reinforcement\n           │   service)    │                                 │ resets clock\n           └───────┬───────┘                                 │\n                   │                                         ▼\n         0.70–0.85 │                                ┌──────────┐\n                   └───────────────────────────────►│ WARM     │\n                                                    │(Staging) │\n         <0.70                                      │  D1 only │\n            │                                        └──────────┘\n            ▼                                          │ 30 days no access\n      ┌──────────┐                                     ▼\n      │  DISCARD │                            ┌──────────┐\n      │  (R2     │                            │ COLD     │\n      │  audit)  │                            │(Archived)│\n      └──────────┘                            │  R2      │\n                                              └──────────┘\n                                                   │ 90+ days with accuracy\n                                                   ▼\n                                            ┌──────────┐\n                                            │ PERMANENT│\n                                            │  D1+KV   │\n                                            └──────────┘\n                                                   │\n                                                   ▼\n                                            ┌──────────┐\n                                            │ DISCARDED│\n                                            │  (R2)    │\n                                            └──────────┘\n```\n\n**Memory Triage Rules (User's Language):**\n\n| Source                     | Initial Tier | Promotion Rule                                                | Final State                    |\n| -------------------------- | ------------ | ------------------------------------------------------------- | ------------------------------ |\n| **User-created decisions** | Hot (0.85)   | Immediate → Permanent                                         | Permanent (never discard)      |\n| **AI-observed patterns**   | Hot (0.70)   | 7 repetitions → Warm; 30 → Cold; 90 with accuracy → Permanent | Permanent or Discarded         |\n| **One-off events**         | Hot (0.70)   | No repetition → Decay                                         | Discarded after 7 days         |\n| **External data**          | Hot          | Follow retention policy                                       | Per policy                     |\n| **Triage Worker**          | Runs nightly | Evaluates all memory against rules                            | Promotes, demotes, or discards |\n\n**Technical Implementation:** The user's \"Hot/Warm/Cold/Permanent/Discard\" model maps to our technical tiers as follows:\n\n- Hot = `active` tier (D1 + KV + Vectorize)\n- Warm = `staging` tier (D1 only)\n- Cold = `archived` tier (D1 + R2 offload)\n- Permanent = `active` tier with `valid_until = null` and `archiveThreshold = 0`\n- Discard = written to R2 cold storage for compliance, then purged from D1\n\n**Memory Interfaces:**\n\n```typescript\nexport type MemoryScope = \"personal\" | \"work\" | \"organization\" | \"ai\";\nexport type MemoryTier = \"active\" | \"staging\" | \"archived\"; // Hot, Warm, Cold\n\nexport interface MemoryRecord {\n  id: string;\n  tenantId: string;\n  scope: MemoryScope;\n  ownerId: string;\n  tier: MemoryTier;\n  content: string;\n  embedding?: number[]; // Vectorize embedding for semantic retrieval\n  confidence: number; // Triage-assigned confidence\n  source: string; // 'sync', 'user_action', 'twin_proposal', 'act_writeback'\n  ssocReferences: string[]; // SSOC IDs referenced\n  accessCount: number;\n  lastAccessedAt?: Date;\n  createdAt: Date;\n  decayAt?: Date; // When auto-archive\n}\n\nexport interface MemoryStoreAPI {\n  store(record: Omit<MemoryRecord, \"id\">): Promise<MemoryRecord>;\n  query(q: MemoryQuery): Promise<MemoryRecord[]>; // semantic + keyword hybrid\n  reinforce(memoryId: string): Promise<void>; // reset decay clock\n  runDecay(tenantId: string): Promise<{ archived: number; decayed: number }>;\n  promote(\n    memoryId: string,\n    fromScope: MemoryScope,\n    toScope: MemoryScope,\n    approvedBy: string\n  ): Promise<void>;\n}\n```\n\n**Decay Rule:** `decay_score = f(time_since_access, reinforcement, confidence, scope_weight)`\n\n- If `decay_score < 0.30` AND `last_accessed > 30 days`: → tier = 'archived', content offloaded to R2\n- If reinforced: `decay_score += 0.2`, reset access clock\n- Triage Worker runs nightly (cron trigger)\n\n→ See §10.2 for Memory schema definitions.\n→ See §13.8 for the Continuity Orchestrator pipeline that runs decay.\n\n### 5.7 Knowledge (RAG Pipeline)\n\nKnowledge is the **document and semantic retrieval layer**. It stores tenant-specific documents (SOPs, contracts, playbooks), indexes them via Vectorize, and serves them to the Twin during Context Assembly.\n\n**Knowledge Architecture:**\n\n```\nDocuments ──► Chunking ──► Embedding ──► Vectorize ──► Retrieval\n   │            │            │             │              │\n   │            │            │             │              ▼\n   │            │            │             │      ┌───────────────┐\n   │            │            │             │      │  AI Search    │\n   │            │            │             │      │  (semantic)   │\n   │            │            │             │      └───────────────┘\n   ▼            ▼            ▼             ▼              ▼\n┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐  ┌───────────────┐\n│  R2    │  │Chunking│  │Embedding│  │Vectorize│  │  Context     │\n│(blobs) │  │(think) │  │(think) │  │(index) │  │  Assembly    │\n└────────┘  └────────┘  └────────┘  └────────┘  └───────────────┘\n```\n\n**Chunking Strategy:**\n\n- Semantic chunking (sentence boundaries) for prose\n- Structural chunking (headers) for docs/SOPs\n- Sliding window with 20% overlap for code/markdown\n\n**Embedding:** gpt-5-mini via OpenRouter (default) or tenant-configured\n\n**Retrieval:** Hybrid (keyword AI Search + semantic Vectorize)\n\n```typescript\nexport interface KnowledgeDocument {\n  id: string;\n  tenantId: string;\n  title: string;\n  source: \"upload\" | \"sync\" | \"twin_generated\" | \"user_created\";\n  mimeType: string;\n  r2Key: string; // Location in R2\n  chunks: KnowledgeChunk[];\n  metadata: Record<string, unknown>;\n  createdBy: string;\n  createdAt: Date;\n  lastIndexedAt?: Date;\n}\n\nexport interface KnowledgeChunk {\n  id: string;\n  documentId: string;\n  tenantId: string;\n  content: string;\n  embedding?: number[];\n  startIndex: number;\n  endIndex: number;\n  metadata: { heading?: string; pageNumber?: number };\n}\n```\n\n### 5.8 Memory Triage (Hot → Warm → Cold → Permanent → Discard)\n\nThe Memory Triage is the **nightly worker** that evaluates all memory against the user's triage rules. It is implemented in the `continuity` service.\n\n**Triage Algorithm:**\n\n```\nFOR each memory record in tenant:\n  1. IF user_created_decision → tier = permanent, valid_until = null\n  2. IF ai_observed_pattern:\n       repetitions = count_reinforcements(memory_id)\n       IF repetitions >= 7 → tier = warm\n       IF repetitions >= 30 → tier = cold\n       IF repetitions >= 90 AND accuracy > 0.85 → tier = permanent\n  3. IF one_off_event AND last_accessed > 7_days → tier = discard\n  4. IF external_data → follow retention_policy\n  5. Update decay_score based on time_since_access + reinforcement\n  6. IF decay_score < archive_threshold:\n       tier = archived\n       content offloaded to R2\n       INSERT decay_log\n  7. WRITE spine_audit_log (action: 'memory_triage')\n```\n\n**Triage Worker Output:**\n\n- Promoted memories: count by scope and tier\n- Archived memories: count and total content size\n- Discarded memories: count (written to R2 for compliance)\n- Audit trail: every triage decision logged\n\n→ See §13.8 for the Continuity Orchestrator pipeline that runs the Triage Worker.\n\n---\n\n## 6. Governance Layer (L5 — Are You Allowed?)\n\n> _\"Governance is the airlock between reasoning and execution. It enforces the 0.70/0.85 confidence law. It runs the HITL orchestrator. It maintains the audit trail for every decision.\"_\n\n### 6.1 Governance Rules Engine\n\nGovernance is the **approval and confidence-scoring layer** that sits between reasoning (Twin) and execution (Act). It enforces the 0.70/0.85 confidence law. It runs the HITL orchestrator. It maintains the audit trail for every decision.\n\n```typescript\nexport interface GovernanceRule {\n  id: string;\n  tenantId: string;\n  name: string;\n  scope: \"global\" | \"capability\" | \"provider\" | \"entity\";\n  target: string; // e.g., 'salesforce.opportunity.update' or 'account'\n  condition: RuleCondition;\n  action: \"auto_approve\" | \"require_approval\" | \"block\" | \"escalate\";\n  confidenceOverride?: number; // Override default 0.70/0.85 for this rule\n  expiresAt?: Date;\n  createdBy: string;\n  createdAt: Date;\n}\n\nexport interface GovernanceDecision {\n  proposalId: string;\n  tenantId: string;\n  userId: string;\n  capability: string;\n  confidence: number;\n  decision: \"auto_approved\" | \"queued\" | \"discarded\" | \"rule_blocked\" | \"escalated\";\n  ruleId?: string;\n  reason: string;\n  timestamp: Date;\n}\n\nexport interface GovernAPI {\n  evaluate(proposal: ActionProposal): Promise<GovernanceDecision>;\n  setRule(rule: GovernanceRule): Promise<void>; // admin only\n  deleteRule(tenantId: string, ruleId: string): Promise<void>;\n  getPendingProposals(tenantId: string, userId: string): Promise<ActionProposal[]>;\n  resolveProposal(\n    proposalId: string,\n    resolution: \"approve\" | \"reject\" | \"modify\",\n    resolverId: string,\n    modifiedPayload?: unknown\n  ): Promise<void>;\n}\n```\n\n### 6.2 The 0.70 / 0.85 Confidence Law\n\nThis law governs **two triage gates** that run on the same thresholds but for different purposes.\n\n**Gate 1: Governance Triage (Execution Gate):**\n\n| Confidence      | Action           | Destination                | Human Touch                | Audit                                  |\n| --------------- | ---------------- | -------------------------- | -------------------------- | -------------------------------------- |\n| **≥ 0.85**      | Auto-approve     | `ACT_QUEUE`                | None                       | `governance_audit_log` (auto-approved) |\n| **0.70 – 0.85** | Queue for review | **My Desk** (owner review) | Approve / Modify / Dismiss | `governance_audit_log` (pending)       |\n| **< 0.70**      | Discard          | —                          | None                       | `governance_audit_log` (discarded)     |\n\n**Gate 2: Memory Triage (Knowledge Gate):**\n\n| Confidence      | Action       | Destination     | Where                              |\n| --------------- | ------------ | --------------- | ---------------------------------- |\n| **≥ 0.85**      | Auto-promote | Active memory   | `continuity` (D1 + KV + Vectorize) |\n| **0.70 – 0.85** | Staging      | Staging memory  | `continuity` (D1 only)             |\n| **< 0.70**      | Discard      | R2 cold storage | `continuity` (compliance/audit)    |\n\n**Rule:** Even Customer-Zero cannot bypass hard approval-expiry gates. Proposals in My Desk expire after 72 hours if not reviewed.\n\n**Confidence Scoring Factors:**\n\n- Twin reasoning quality (model evaluation)\n- Historical success rate (memory lookup)\n- Rule match severity (governance rules)\n- Data freshness (entity `lastModifiedAt`)\n- User preference alignment (personal memory)\n\n### 6.3 HITL Orchestrator (5-Step Flow)\n\nThe HITL orchestrator manages the **My Desk** approval interface. It is implemented as a Durable Object per `tenant_id + user_id` — never global.\n\n**HITL Flow:**\n\n```\n                    ┌─────────────┐\n                    │   PENDING   │◄─────────────────┐\n                    │  (0.70-0.85)│                  │\n                    └──────┬──────┘                  │\n                           │                        │\n              ┌────────────┼────────────┐           │\n              ▼            ▼            ▼           │\n        ┌─────────┐  ┌─────────┐  ┌─────────┐      │\n        │APPROVED │  │REJECTED │  │ EXPIRED │      │\n        └────┬────┘  └─────────┘  └─────────┘      │\n             │                                      │\n             ▼                                      │\n        ┌─────────┐                                 │\n        │EXECUTED │                                 │\n        └─────────┘                                 │\n                                                    │\n   ┌────────────────────────────────────────────────┘\n   │\n   │   Auto-path (≥ 0.85):\n   │   PENDING ──► AUTO_APPROVED ──► EXECUTED\n   │\n   │   Discard path (< 0.70):\n   │   PENDING ──► DISCARDED\n   │\n   └────────────────────────────────────────────────►\n```\n\n**HITL Durable Object:**\n\n```typescript\nexport class HITLOrchestrator implements DurableObject {\n  // CRITICAL: DO ID = idFromName(tenant_id + \":\" + user_id), NEVER \"global\"\n\n  async queueProposal(proposal: ActionProposal): Promise<void> {\n    // 1. Store in DO state\n    await this.state.storage.put(`proposal:${proposal.id}`, {\n      ...proposal,\n      status: \"pending\",\n      queuedAt: Date.now(),\n    });\n    // 2. Set alarm for expiry (72 hours)\n    await this.state.storage.setAlarm(Date.now() + 72 * 60 * 60 * 1000);\n    // 3. Notify user (SSE push, email, or Slack)\n    await notifyUser(this.tenantId, this.userId, {\n      type: \"proposal_queued\",\n      proposalId: proposal.id,\n      summary: proposal.summary,\n    });\n  }\n\n  async alarm(): Promise<void> {\n    // Expire old proposals\n    const proposals = await this.state.storage.list<ActionProposal>({ prefix: \"proposal:\" });\n    for (const [key, proposal] of proposals) {\n      if (proposal.status === \"pending\" && Date.now() - proposal.queuedAt > 72 * 60 * 60 * 1000) {\n        proposal.status = \"expired\";\n        await this.state.storage.put(key, proposal);\n        await auditLog({\n          tenantId: this.tenantId,\n          action: \"proposal_expired\",\n          proposalId: proposal.id,\n          via: \"govern\",\n        });\n      }\n    }\n  }\n}\n```\n\n**My Desk Interface:**\n\n- List pending proposals for the current user\n- Approve → enqueues to `ACT_QUEUE`\n- Reject → updates proposal status, logs to `governance_audit_log`\n- Modify → updates payload, re-evaluates confidence, then approves or re-queues\n- Dismiss → silently discards (still logged)\n\n→ See §10.4 for Governance schema (proposals, audit log, HITL sessions).\n→ See §11.2 (P0-4) for HITL DO security requirement.\n\n### 6.4 Audit & Compliance\n\nAudit is **immutable, fail-loud, and cross-cutting**. Compliance is enforced by the audit trail, tenant isolation, and data retention policies.\n\n**Audit Tables:**\n\n| Table                  | What It Logs                                       | Failure Behavior              | Retention |\n| ---------------------- | -------------------------------------------------- | ----------------------------- | --------- |\n| `spine_audit_log`      | Entity changes, MCP access, reads, writes          | Write failure → halt sync     | 7 years   |\n| `governance_audit_log` | Approval decisions (approve/reject/discard/expire) | Write failure → halt proposal | 7 years   |\n| `twin_audit_events`    | Reasoning trace, model calls, confidence scores    | Write failure → halt proposal | 90 days   |\n| `outbound_mcp_calls`   | Provider calls, latency, errors                    | Write failure → circuit break | 90 days   |\n| `normalizer_dlq`       | Rejected records with error context                | Write failure → alert         | 7 years   |\n\n**Audit Interface:**\n\n```typescript\nexport interface AuditEvent {\n  id: string; // ULID\n  tenantId: string;\n  timestamp: Date;\n  action: string;\n  actor: string; // 'user:<id>', 'twin', 'agent:<id>', 'system'\n  resource?: string; // SSOC ID or capability name\n  via: string; // 'mcp', 'webhook', 'sync', 'act', 'govern', 'normalizer'\n  payloadHash?: string; // SHA-256\n  metadata?: Record<string, unknown>;\n  gdprCategory?: \"personal_data\" | \"sensitive_data\" | \"anonymized\";\n  dataSubjectId?: string; // For GDPR subject access requests\n  retentionPolicy: \"standard\" | \"extended\" | \"permanent\";\n}\n\n// CRITICAL: Audit write failure stops the operation\nexport async function auditLog(event: Omit<AuditEvent, \"id\" | \"timestamp\">): Promise<void> {\n  const fullEvent: AuditEvent = { ...event, id: generateUlid(), timestamp: new Date() };\n  try {\n    await db.insert(auditLogs).values(fullEvent);\n  } catch (err) {\n    // FAIL-LOUD: If we cannot audit, we do not proceed\n    throw new AuditError(\"AUDIT_WRITE_FAILURE\", 500, {\n      message: \"Audit log write failed; operation halted for compliance\",\n      originalEvent: fullEvent,\n    });\n  }\n}\n```\n\n**Compliance Boundaries:**\n\n- **Tenant Isolation:** Every query: `WHERE tenant_id = ?`. Cross-tenant: 403 (fail-loud).\n- **Data Residency:** Default = Cloudflare edge global. Enterprise = pin to jurisdiction (EU, US, AU).\n- **Encryption:** At-rest (D1 encrypted by Cloudflare). In-transit (TLS 1.3). Credentials (CF Secrets).\n- **Retention:** Active memory: 30 days (with reinforcement). Audit: 7 years. Spine: Permanent (soft-delete).\n- **GDPR:** Article 17 (Right to Erasure) → anonymize audit subjects; keep audit trail. Article 20 (Portability) → export API.\n\n→ See §11.4 for Compliance framework (SOC 2, GDPR, HIPAA).\n→ See §11.10 for Audit model details.\n\n### 6.5 Agentic vs Prompt Classification\n\nThis is a **critical conceptual boundary** that determines governance, audit, and security requirements.\n\n| Dimension            | Prompt                   | Enrichment (Gray Area)           | Agentic                         |\n| -------------------- | ------------------------ | -------------------------------- | ------------------------------- |\n| **Reads from Spine** | Yes                      | Yes                              | Yes                             |\n| **Writes to Spine**  | No                       | Yes (structured data only)       | Yes (or calls tools)            |\n| **Calls tools**      | No                       | No                               | Yes                             |\n| **Side effects**     | None                     | Limited (structured writeback)   | Yes (external actions)          |\n| **Governance**       | None                     | None (auto-approved)             | 0.70/0.85 confidence law        |\n| **Approval**         | None                     | None                             | HITL for 0.70–0.85              |\n| **Audit**            | Minimal (query log)      | Full (write + actor)             | Full (proposal + execution)     |\n| **Examples**         | \"Summarize this account\" | \"Extract key phrases from notes\" | \"Update Salesforce opportunity\" |\n| **Data Access**      | L1-L2                    | L1-L3                            | L1-L4                           |\n\n**Prompt:** Reads from Spine, no tool calls, no side effects, no governance. Example: \"What's the status of Acme Corp?\"\n\n**Enrichment (Gray Area):** LLM reads + writes structured data to Spine, auto-approved. Example: \"Summarize the last 10 emails and save the summary to the account's `notes` field.\" No external tool calls. Deterministic given input. Still logged to `spine_audit_log` with `actor_type: 'twin'`.\n\n**Agentic:** Writes to Spine or calls tools, side effects exist, governance applies, approval may be needed. Example: \"Create a new opportunity in Salesforce and notify the team in Slack.\" This requires governance evaluation (confidence scoring), may go to My Desk, and is fully audited.\n\n**Why This Matters:** The classification determines whether a user's request goes through the fast path (prompt, <100ms) or the full OODA loop (agentic, 1–10s with governance). It also determines liability: agentic actions are governed; prompt responses are not.\n\n---\n\n## 7. Provider Fabric (L6 — Who Executes?)\n\n> _\"The Provider Fabric makes the platform provider-agnostic at the Continuity Layer. Swapping Salesforce for HubSpot, or Nango for direct OAuth, or MCP for REST, is a configuration change — never a consumer-facing change.\"_\n\nThe Provider Fabric is Layer 6 — the **execution boundary** between the platform's continuity kernel and the external universe of SaaS providers, APIs, databases, queues, and agent runtimes. Every byte that crosses this boundary is mediated by an adapter, governed by a credential wall, enriched by Entity360 context, and monitored by health probes and circuit breakers.\n\n**Core invariant:** No service outside the Provider Fabric ever holds a provider credential, constructs a provider API call, or parses a provider-specific response schema. The Continuity Layer is entirely provider-agnostic.\n\n### 7.1 Adapter Pattern (Nango / MCP / Native)\n\nThe Integration Manager is **not a separate service** in v2.0. It is a **pattern** — a set of adapter modules co-located inside the services that actually touch external providers.\n\n**Adapter Philosophy:**\n\n| Principle              | Implementation                                                                              |\n| ---------------------- | ------------------------------------------------------------------------------------------- |\n| **Co-location**        | Adapters live inside the service that uses them (`act`, `connector`, `connector-sync`)      |\n| **Zero extra hop**     | `act` → adapter → provider. No intermediate orchestrator.                                   |\n| **Interface contract** | `OutboundAdapter` and `InboundSyncAdapter` interfaces are the only contracts.               |\n| **Swappability**       | Change adapter registration in a map. No consumer code changes.                             |\n| **Tenant isolation**   | Every adapter instance is scoped to one tenant. No shared credential state.                 |\n| **Observability**      | Every adapter call emits a `provider_call` telemetry event with latency, status, tenant_id. |\n\n**Outbound Adapter Contract:**\n\n```typescript\nexport interface OutboundAdapter {\n  execute(request: OutboundExecutionRequest): Promise<OutboundExecutionResult>;\n  healthCheck(credentials: Credentials): Promise<ProviderHealthStatus>;\n  executeBatch?(requests: OutboundExecutionRequest[]): Promise<OutboundExecutionResult[]>;\n}\n\nexport interface OutboundExecutionRequest {\n  capability: string; // \"salesforce.opportunity.update\"\n  tenantId: string; // Every call is tenant-scoped\n  payload: Record<string, unknown>;\n  entityContext?: Entity360Context; // Injected by Continuity Layer\n  correlationId: string;\n  approvalToken?: string; // For writes\n  timeoutMs?: number; // Default: 30000\n  idempotencyKey: string; // For safe retries\n}\n\nexport interface OutboundExecutionResult {\n  status: \"success\" | \"error\" | \"partial\";\n  rawResponse: Record<string, unknown>;\n  affectedEntities?: AffectedEntity[]; // For Spine writeback\n  metadata: {\n    provider: string;\n    adapter: \"nango\" | \"mcp\" | \"native\";\n    latencyMs: number;\n    retryCount: number;\n    circuitBreakerState: CircuitBreakerState;\n    timestamp: number;\n  };\n  error?: AdapterError;\n}\n```\n\n**Inbound Sync Adapter Contract:**\n\n```typescript\nexport interface InboundSyncAdapter {\n  fetch(request: InboundSyncRequest): Promise<InboundSyncResult>;\n  handleWebhook?(request: WebhookRequest): Promise<WebhookResult>;\n  getCursor?(credentials: Credentials): Promise<string | null>;\n}\n```\n\n**Adapter Registry (the ONLY place provider → adapter mapping is defined):**\n\n```typescript\n// Outbound Adapter Registry\n  // Nango-powered OAuth providers (mature SaaS)\n  salesforce: {\n    adapter: NangoAdapter,\n    credentialType: \"nango\",\n    defaultTimeoutMs: 30000,\n    supportsBatch: true,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 30000, halfOpenMaxCalls: 3 },\n  },\n  hubspot: {\n    adapter: NangoAdapter,\n    credentialType: \"nango\",\n    defaultTimeoutMs: 30000,\n    supportsBatch: true,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 30000, halfOpenMaxCalls: 3 },\n  },\n  slack: {\n    adapter: NangoAdapter,\n    credentialType: \"nango\",\n    defaultTimeoutMs: 15000,\n    supportsBatch: false,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 20000, halfOpenMaxCalls: 2 },\n  },\n\n  // MCP-native providers (AI-first tool APIs)\n  github: {\n    adapter: MCPOutboundAdapter,\n    credentialType: \"mcp\",\n    defaultTimeoutMs: 30000,\n    supportsBatch: false,\n    circuitBreaker: { failureThreshold: 3, recoveryTimeoutMs: 60000, halfOpenMaxCalls: 1 },\n  },\n  linear: {\n    adapter: MCPOutboundAdapter,\n    credentialType: \"mcp\",\n    defaultTimeoutMs: 20000,\n    supportsBatch: true,\n    circuitBreaker: { failureThreshold: 3, recoveryTimeoutMs: 60000, halfOpenMaxCalls: 1 },\n  },\n  figma: {\n    adapter: MCPOutboundAdapter,\n    credentialType: \"mcp\",\n    defaultTimeoutMs: 20000,\n    supportsBatch: false,\n    circuitBreaker: { failureThreshold: 3, recoveryTimeoutMs: 60000, halfOpenMaxCalls: 1 },\n  },\n\n  // Native REST providers (direct HTTP, no abstraction)\n  stripe: {\n    adapter: NativeAdapter,\n    credentialType: \"native\",\n    defaultTimeoutMs: 30000,\n    supportsBatch: true,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 30000, halfOpenMaxCalls: 3 },\n  },\n  aws: {\n    adapter: NativeAdapter,\n    credentialType: \"native\",\n    defaultTimeoutMs: 60000,\n    supportsBatch: true,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 60000, halfOpenMaxCalls: 3 },\n  },\n  custom: {\n    adapter: NativeAdapter,\n    credentialType: \"native\",\n    defaultTimeoutMs: 30000,\n    supportsBatch: false,\n    circuitBreaker: { failureThreshold: 5, recoveryTimeoutMs: 30000, halfOpenMaxCalls: 3 },\n  },\n};\n```\n\nChanging a provider from Nango to MCP is a **one-line change** in the registry. No consumer code changes. No workbench changes. No Twin changes.\n\n### 7.2 Outbound Execution Flow\n\nThe `act` service is the outbound execution engine. It is a Cloudflare Worker that consumes `ACT_QUEUE`.\n\n**Execution Flow (10 steps):**\n\n```\nACT_QUEUE\n    │\n    ▼\n┌─────────────────────────────────────────────────────────────┐\n│  iw-act  (Cloudflare Worker)                                 │\n│                                                              │\n│  1. DEQUEUE          → Parse message                       │\n│  2. IDEMPOTENCY      → Check KV; return cached if exists     │\n│  3. CREDENTIAL WALL  → getCredentials(provider, tenantId)  │\n│  4. CIRCUIT BREAKER  → Check state; reject if OPEN         │\n│  5. ENTITY360        → assembleEntity360(tenantId, entityId)│\n│  6. ADAPTER SELECT   → selectAdapter(provider)             │\n│  7. ADAPTER EXECUTE  → Call provider API                   │\n│  8. TELEMETRY        → emitProviderCall()                  │\n│  9. CIRCUIT UPDATE   → Record success/failure              │\n│  10. SPINE WRITEBACK  → enqueueToPipelineQueue()             │\n│  11. IDEMPOTENCY     → Cache result in KV (24h)             │\n└─────────────────────────────────────────────────────────────┘\n```\n\n**Key Security Rules:**\n\n- NEVER log tokens. NEVER return credentials to caller.\n- Every adapter call is tenant-scoped.\n- Every result writes back to Spine via Pipeline (act NEVER writes D1 directly).\n- Even errors write to pipeline for audit completeness.\n\n→ See §7.4 for the Credential Wall.\n→ See §7.7 for the Circuit Breaker.\n→ See §11.3 for Provider Fabric security boundaries.\n\n### 7.3 Inbound Sync Flow\n\nInbound data flows from providers through adapters to the Normalizer to the Spine.\n\n```\nProvider Webhooks / Sync Polls / File Drops\n       │\n       ▼\n┌─────────────────┐\n│ webhook-ingress │  verify signature, route to tenant\n└────────┬────────┘\n         ▼\n┌─────────────────┐\n│ connector-sync  │  Sync adapter selection, credential fetch\n│  (Worker)       │  Raw fetch from provider API\n└────────┬────────┘\n         ▼\n  CONNECTOR_SYNC_QUEUE\n         │\n         ▼\n┌─────────────────┐\n│  connector-sync │  Load sync state, fetch records, persist cursor\n│  (Worker)       │  Enqueue to NORMALIZER_QUEUE\n└────────┬────────┘\n         ▼\n  NORMALIZER_QUEUE\n         │\n         ▼\n┌─────────────────┐\n│  normalizer     │  8-stage pipeline (NA0–NA5)\n│  (Worker)       │  Enqueue to PIPELINE_QUEUE\n└────────┬────────┘\n         ▼\n  PIPELINE_QUEUE\n         │\n         ▼\n┌─────────────────┐\n│  pipeline       │  ONLY writer to D1 Spine\n│  (Sole Writer)  │  INSERT entities, relationships, audit log\n└─────────────────┘\n```\n\n**Webhook-to-Sync Routing:** When a provider webhook arrives, it is NOT directly processed. It is routed through the same adapter pattern: verify signature → extract tenant → enqueue to connector-sync → process inline (fast). The webhook ingress Worker returns 202 immediately; processing is async.\n\n### 7.4 Credential Wall\n\nThe Credential Wall is the **single point of secret retrieval** for the entire Provider Fabric. No adapter fetches credentials directly. No service caches credentials outside the wall. Every credential access is logged, audited, and tenant-isolated.\n\n**Storage Hierarchy:**\n\n```\nTIER 1: Cloudflare Secrets (Encrypted at rest)\n  Secret Name Pattern: PROVIDER_{PROVIDER}_{TENANT_ID}_API_KEY\n  Examples: PROVIDER_SALESFORCE_T_abc123_API_KEY\n  Access: Only via env.getSecret() in Workers\n  Rotation: Admin CLI or Nango auto-refresh\n\nTIER 2: Nango Vault (OAuth token storage)\n  connectionId = tenant_id (by convention)\n  Stores: access_token (~1h), refresh_token (long-lived, rotated by Nango), expires_at, scopes\n  Access: Nango REST API with NANGO_SECRET_KEY\n\nTIER 3: KV Cache (Performance, NOT security)\n  Key: cred_cache:{provider}:{tenantId}\n  TTL: 300 seconds (5 min)\n  Content: Decrypted access token (ephemeral)\n  WARNING: KV is NOT encrypted. Never cache refresh tokens in KV.\n```\n\n**getCredentials() Flow:**\n\n1. Check KV cache (fast path)\n2. Check credential type from connector config:\n   - `nango` → call Nango API for access token\n   - `native` → fetch from CF Secrets\n   - `mcp` → fetch endpoint + token from Secrets\n3. If token expired and refreshable: Nango auto-refresh, or native refresh_token → new access_token\n4. Cache in KV (5 min TTL)\n5. Audit log: every retrieval logged to `spine_audit_log` with `action: CREDENTIAL_ACCESS`\n\n→ See §11.9 for Secret Management details.\n→ See §11.3 for Cross-tenant prevention in credential access.\n\n### 7.5 Entity360 Injection\n\nEvery outbound call carries **context** from the Continuity Layer. This is not just \"pass the entity ID.\" It is a structured, ranked, truncated context package.\n\n**Entity360 Assembly Pipeline (5 Steps):**\n\n1. **Primary Entity Resolution:** Query Spine for entity by SSOC UUID\n2. **Related Entities (max 5):** Query relationships table, rank by strength\n3. **Timeline (last 10 events):** Query audit log, normalize to TimelineEvent[]\n4. **Memory Context:** Query continuity service for { lastInteraction, sentiment, keyTopics, openTasks }\n5. **Context Window Optimization:** Truncate to fit within 8KB budget per outbound call\n\n**Entity360Context Output:**\n\n```typescript\nexport interface Entity360Context {\n  primaryEntity: { id: string; type: string; providerId: string; displayName: string };\n  relatedEntities: RelatedEntity[]; // max 5\n  timeline: TimelineEvent[]; // max 10\n  memory: { lastInteraction: number; sentiment: number; keyTopics: string[]; openTasks: number };\n  projectionContext?: Record<string, unknown>;\n}\n```\n\nFor MCP outbound adapters, Entity360 is injected into the tool call as `_iw_context` parameter.\n\n→ See §4.3 for Context Assembly in the Capability Layer.\n→ See §5.1 for the Spine that Entity360 queries.\n\n### 7.6 Tool-to-Tool Communication Protocol\n\nThe Tool-to-Tool (T2T) protocol enables **providers to communicate with each other through the Bridge**, not peer-to-peer. When Salesforce needs to notify Slack, or GitHub needs to update Linear, the request flows through the Provider Fabric — mediated, governed, and audited.\n\n**Why Not Peer-to-Peer?**\n\n| Peer-to-Peer        | T2T via Bridge                                |\n| ------------------- | --------------------------------------------- |\n| No governance       | Governed by confidence gate                   |\n| No audit trail      | Full `spine_audit_log` + `outbound_mcp_calls` |\n| No context sharing  | Entity360 injected automatically              |\n| Credential sprawl   | Single credential wall                        |\n| No tenant isolation | `WHERE tenant_id = ?` on every hop            |\n\n**T2T Example: Salesforce opportunity closes → Slack notification**\n\n```\nSalesforce webhook → webhook-ingress → connector-sync → normalizer → pipeline → Spine\n     → twin-orchestrator (AMBIENT) detects pattern: \"closed_won → notify channel\"\n     → Proposes: send_slack_notification (confidence: 0.92)\n     → govern (≥0.85 → AUTO-APPROVE)\n     → ACT_QUEUE → act → NangoAdapter (Slack) → Slack API\n     → pipeline writeback → continuity memory compound\n```\n\n**T2T Message Schema:**\n\n```typescript\nexport interface T2TRequest {\n  sourceProvider: string;\n  targetCapability: string;\n  tenantId: string;\n  triggerEntityId: string;\n  sourceContext: Entity360Context;\n  proposedPayload: Record<string, unknown>;\n  reasoningTrace: string;\n  approvalToken: string;\n  maxChainDepth: number; // Max 3\n  currentDepth: number;\n}\n\nexport interface T2TResult {\n  status: \"success\" | \"error\" | \"rejected\";\n  targetResult?: OutboundExecutionResult;\n  nextSteps?: T2TRequest[];\n  chainDepthConsumed: number;\n  auditRecord: {\n    tenantId;\n    sourceProvider;\n    targetCapability;\n    triggerEntityId;\n    timestamp;\n    correlationId;\n  };\n}\n```\n\n**T2T Governance Rules:**\n\n1. Chain depth limit: Max 3 hops. A → B → C. No further.\n2. Cycle detection: `salesforce → slack → salesforce` is blocked by T2TChainRegistry.\n3. Capability whitelist: Only capabilities in `t2t_allowed_capabilities` config.\n4. Approval required: Every T2T hop re-evaluates confidence. Auto-approve only if ≥0.85.\n5. Audit completeness: Every hop logs to `spine_audit_log` with `via: t2t`.\n6. Tenant isolation: T2T cannot cross tenant boundaries. Ever.\n\n→ See §9.2 for Knowledge Sharing Protocol (KSP).\n→ See §9.3 for Agent-to-Agent Communication (A2A).\n\n### 7.7 Provider Health & Circuit Breaker\n\n**Health Check Architecture:**\n\nA cron runs every 60 seconds per active provider/tenant pair inside the `act` service:\n\n1. Call `adapter.healthCheck(credentials)`\n2. Record latency, status code, error (if any)\n3. Update KV: `health:{provider}:{tenantId}`\n\n**Health State Machine:**\n\n```\n                    ┌─────────────────────────────────────────┐\n                    │                                         │\n                    │   ┌─────────┐      success              │\n           ┌────────┼──►│  CLOSED │◄────────────────────┐     │\n           │        │   └────┬────┘                     │     │\n           │        │        │ failure                   │     │\n           │        │        ▼                           │     │\n           │        │   ┌─────────┐  failure >= threshold│     │\n  recovery │        │   │  COUNT  │──────────────────────┼─────┼──►┌──────┐\n  timeout  │        │   │ FAILURES│                      │     │   │ OPEN │\n  elapsed  │        │   └─────────┘                      │     │   └──┬───┘\n           │        │                                    │     │      │\n           │        └────────────────────────────────────┘     │      │\n           │                                                   │      │\n           │         ┌─────────────────────────────────────────┘      │\n           │         │                                                │\n           │    ┌────┴────┐    halfOpenMaxCalls                      │\n           └───┄│HALF_OPEN│◄─────────────────────────────────────────┘\n                └────┬────┘\n                     │\n        ┌────────────┼────────────┐\n        │ success    │    failure │\n        ▼            │            ▼\n    ┌───────┐        │        ┌──────┐\n    │ CLOSED│        │        │ OPEN │\n    └───────┘        │        └──────┘\n                     │\n```\n\n**Circuit Breaker Rules:**\n\n- `CLOSED` → healthy, traffic allowed\n- `OPEN` → unhealthy, all calls rejected fast (return `CIRCUIT_OPEN` error)\n- `HALF_OPEN` → probing with limited traffic\n- Transitions: CLOSED → OPEN when `consecutiveFailures >= threshold`. OPEN → HALF_OPEN when `recoveryTimeoutMs` elapsed. HALF_OPEN → CLOSED when `halfOpenMaxCalls` consecutive successes. HALF_OPEN → OPEN on any failure during probe.\n\n**KV-Backed State Storage:**\n\n```typescript\ninterface CircuitBreakerStateData {\n  state: \"CLOSED\" | \"OPEN\" | \"HALF_OPEN\";\n  failures: number;\n  lastFailureAt: number;\n  lastSuccessAt: number;\n  openedAt?: number;\n  halfOpenCalls: number;\n  halfOpenSuccesses: number;\n}\n```\n\nKey: `cb:{provider}:{tenantId}` in KV. TTL: 24 hours (survives deploys).\n\n→ See §13.1 for Ingestion Pipeline health monitoring.\n→ See §11.13 for Threat Model (DoS / Abuse mitigation).\n\n---\n\n## 8. The OODA Runtime (Cross-Cutting)\n\n> _\"OODA runs across the Capability Layer and the Continuity Layer. The Capability Layer owns the execution fabric; the Continuity Layer owns the state substrate. The Twin is the ambient reasoning engine that bridges both.\"_\n\nOODA (Observe → Orient → Decide → Act) is not a layer — it is a **cross-cutting decision cycle** that spans the entire platform. Every feature, every pipeline, every agent can be mapped to one or more OODA phases.\n\n### 8.1 Observe → Pipelines (Ingestion, Sync, Webhooks)\n\n**Observe-phase services** ingest signals from the external world without interpretation. They are the eyes and ears of the system.\n\n| Service                   | Role                                                      | OODA Phase | Speed  |\n| ------------------------- | --------------------------------------------------------- | ---------- | ------ |\n| `webhook-ingress`         | Receives provider webhooks, verifies signatures, enqueues | Observe    | ms     |\n| `connector-sync`          | Polls providers on cron, fetches raw records, enqueues    | Observe    | min–hr |\n| `folder-watcher`          | Monitors filesystem changes, emits change events          | Observe    | ms     |\n| `mcp-connector` (inbound) | Receives MCP tool calls from AI assistants                | Observe    | ms     |\n| `platform.signals.*`      | Query capabilities for signal detection                   | Observe    | ms     |\n\n**Observe → Orient Handoff:** Raw signals are enqueued to `NORMALIZER_QUEUE` (for structured data) or `MEMORY_INTAKE_QUEUE` (for unstructured signals). The handoff is queue-based, not synchronous, to allow backpressure and retries.\n\n### 8.2 Orient → Agents (Context Assembly, Twin, Knowledge)\n\n**Orient-phase services** interpret observed signals, assemble context, and prepare for decision-making. They are the brain of the system.\n\n| Service                         | Role                                               | OODA Phase | Speed                 |\n| ------------------------------- | -------------------------------------------------- | ---------- | --------------------- |\n| `intelligence`                  | Context Assembly — Entity360, Memory, Knowledge    | Orient     | <2s (cache hit <10ms) |\n| `knowledge`                     | RAG, semantic search, document indexing            | Orient     | <2s                   |\n| `continuity`                    | Memory retrieval, SSOC resolution, graph traversal | Orient     | <10ms (D1)            |\n| `normalizer`                    | 8-stage canonicalization (NA0–NA5)                 | Orient     | <2s per batch         |\n| `platform.entity360.get`        | Capability: fetch full context for an entity       | Orient     | <100ms                |\n| `intelligence.context.assemble` | Capability: assemble full context window           | Orient     | <2s                   |\n\n**Orient → Decide Handoff:** Assembled context is passed to `twin-orchestrator` (for reasoning) or directly to `govern` (for pre-scored actions). The handoff is via service binding (synchronous for cache hits) or queue (asynchronous for complex reasoning).\n\n### 8.3 Decide → Hybrid (Capability Routing + Governance)\n\n**Decide-phase services** evaluate options, score confidence, and produce proposals. They are the judgment of the system.\n\n| Service             | Role                                       | OODA Phase | Speed                      |\n| ------------------- | ------------------------------------------ | ---------- | -------------------------- |\n| `twin-orchestrator` | Persistent per-user DO — reasoning engine  | Decide     | 1–2s simple, 5–10s complex |\n| `think`             | LLM orchestration — OpenRouter abstraction | Decide     | 1–5s                       |\n| `govern`            | Confidence scoring, proposal evaluation    | Decide     | <100ms                     |\n| `twin.propose`      | Capability: generate proposal              | Decide     | 1–5s                       |\n| `twin.analyze`      | Capability: analyze entity                 | Decide     | 1–2s                       |\n| `twin.predict`      | Capability: generate forecast              | Decide     | 2–5s                       |\n\n**Decide → Act Handoff:** Proposals with confidence ≥ 0.85 go directly to `ACT_QUEUE`. Proposals with confidence 0.70–0.85 go to My Desk (HITL). Proposals with confidence < 0.70 are discarded. The handoff is via `ACT_QUEUE` (for auto-approved) or `GOVERNANCE_QUEUE` (for HITL).\n\n### 8.4 Act → Pipelines + Agents (Execution + Writeback)\n\n**Act-phase services** execute approved actions and write results back to the Spine. They are the hands of the system.\n\n| Service                    | Role                                                 | OODA Phase | Speed                   |\n| -------------------------- | ---------------------------------------------------- | ---------- | ----------------------- |\n| `hermes`                   | Message queue + execution routing                    | Act        | <10ms routing           |\n| `workflow`                 | Durable orchestration — sagas, retries, pause/resume | Act        | 500ms–2s per step       |\n| `act`                      | Outbound execution + adapter modules                 | Act        | 500ms–2s (external API) |\n| `iw-agent-runtime`         | MCP-compliant agent execution                        | Act        | 1–5s                    |\n| `mcp-connector` (outbound) | MCP calls via act adapters                           | Act        | 1–5s                    |\n| `platform.execute.*`       | Generic execution capability                         | Act        | Varies                  |\n\n**Act → Continuity Handoff:** Execution results are enqueued to `PIPELINE_QUEUE`. The `pipeline` service writes to Spine, logs to `spine_audit_log`, and updates KV caches. This closes the OODA loop — the action is now part of the observed state for the next cycle.\n\n### 8.5 Learn → Memory Compounding (Continuity Engine)\n\n**Learn is the secret fifth phase of OODA.** After every action, the system learns:\n\n1. **Result Analysis:** Was the action successful? Did it produce the expected outcome?\n2. **Memory Reinforcement:** If successful, reinforce the memory that led to the decision. If failed, update the decay score.\n3. **Pattern Extraction:** The Twin extracts patterns from sequences of actions and outcomes.\n4. **Knowledge Update:** New documents, SOPs, or playbooks are indexed into the Knowledge corpus.\n5. **Governance Feedback:** Approved proposals with positive outcomes increase confidence thresholds. Rejected proposals decrease them.\n\n**Learn → Observe Handoff:** The learned context is now part of the Spine. The next observation cycle will see the updated entity, the reinforced memory, and the new pattern. The system is **strictly more informed** than the previous cycle.\n\nThis is why the product promise is true: **Every day the system is smarter. Every day the AI knows more. Every day the user works faster.**\n\n### 8.6 OODA for Connected Tools\n\nWhen a new tool is connected, the OODA cycle runs in fast-forward:\n\n1. **Observe:** Connector sync pulls historical data. Webhooks start arriving. The tool's signals are now part of the observed state.\n2. **Orient:** Normalizer maps tool-specific schemas to canonical types. Entity360 is assembled for the first time. The tool's data is now in the Spine.\n3. **Decide:** The Twin discovers patterns in the new data. Proposals are generated. The tool's capabilities are now in the Capability Fabric.\n4. **Act:** The user acts on the unified data in the workbench. The tool can now be invoked through the Bridge.\n5. **Learn:** Memory compounds. The tool's behavior patterns are learned. The next cycle is faster and more accurate.\n\n**A connected tool is not just \"integrated.\" It is OODA-assimilated.**\n\n---\n\n## 9. The Ecosystem (L0)\n\n> _\"The Ecosystem is not a feature. It is the architecture of scale. When one enterprise learns, all enterprises can benefit — without sharing a single byte of proprietary data.\"_\n\nThe Ecosystem is the outermost layer — the **layer zero** — that enables the IntegrateWise platform to learn across tenants, share knowledge safely, and distribute intelligence. It is the foundation of Collective Intelligence (CI): what the network learns, each individual learns; what each individual learns, the network learns.\n\n### 9.1 Connection Hub (Every Action Triggers a Learning Signal)\n\n**Connection Hub Event Flow:**\n\n```\nUser Action ──► telemetryQueue ──► Telemetry Hub (aggregate)\n                    │                    │\n                    │                    ▼\n                    │            ┌──────────────────┐\n                    │            │ 1. Aggregate       │\n                    │            │    → aggregate<confidence>\n                    │            │ 2. Topology Gen    │\n                    │            │    → topology<cluster>\n                    │            │ 3. Knowledge Sink  │\n                    │            │    → knowledge<compounded>\n                    │            └──────────────────┘\n                    │                    │\n                    │                    ▼\n                    │            ┌──────────────────┐\n                    │            │ Agentic Proposal   │\n                    │            │ (if threshold met) │\n                    │            └──────────────────┘\n                    │                    │\n                    ▼                    ▼\n            ┌──────────────────┐  ┌──────────────────┐\n            │  Signal Broadcast  │  │ Governance Gate  │\n            │  (Tenant Group)    │  │ (Confidence)     │\n            └──────────────────┘  └──────────────────┘\n```\n\n**Telemetry Hub Signal Processing:**\n\n1. Every action → telemetry event with confidence score\n2. Aggregation by capability, provider, entity type → patterns\n3. Topology generation: map tenant behavior to clusters (\"healthcare startups\", \"enterprise sales teams\")\n4. Knowledge compound: extract anonymized best practices, decision patterns\n5. Knowledge sink: if aggregate confidence > 0.85, create `KnowledgeArtifact` (ecosystem-scoped, no tenant data)\n6. Agentic proposal: if aggregate confidence > 0.70, propose action to relevant tenants\n7. Governance gate: evaluate against tenant-specific rules; may require approval\n8. Broadcast: signal is sent to tenants in the same topology cluster via SSE, Slack, or Email\n\n**Event Topology:**\n\n```typescript\nexport interface TelemetryEvent {\n  tenantId: string; // Sender tenant\n  userId: string; // Sender user\n  actionType: string; // 'tool_execution', 'ai_interaction', 'memory_reinforcement', 'knowledge_synthesis'\n  confidence: number; // 0.0–1.0\n  payload: Record<string, unknown>; // Anonymized payload\n  topologyCluster: string; // Cluster label\n  timestamp: Date;\n}\n\nexport interface TelemetryHubConfig {\n  enableSignalBroadcast: boolean; // Default: true (opt-out per tenant)\n  enableTopologyLearning: boolean; // Default: true (opt-out per tenant)\n  enableSignalSummarization: boolean; // Default: true\n  batchingIntervalMs: number; // 5000 (5s)\n  signalEvaluationThreshold: number; // 0.70 (0.70–0.85 = moderate)\n  signalAggregationFactor: number; // 0.85 (≥0.85 = strong)\n  knowledgeAggregationThreshold: number; // 0.85\n  knowledgeSinkBase: number; // 0.85\n  signalRetentionPeriod: number; // 30 days\n  retentionPeriod: number; // 7 days (for telemetry hub)\n  policyRetention: number; // 30 days\n  chainIdFormat: \"guid\"; // Global UUID for cross-tenant traceability\n  agentic: boolean; // true: can generate proposals\n  enableRouting: boolean; // true: route based on topology\n}\n```\n\n### 9.2 Knowledge Sharing Protocol (KSP)\n\nThe KSP enables **safe, permissioned, ephemeral knowledge sharing** between tenants. No tenant data ever crosses boundaries. Only extracted, anonymized, confidence-scored knowledge artifacts are shared.\n\n**KSP Flow (MCP + Bridge):**\n\n```\nTenant A (Source) ──► KSP Compiler ──► Knowledge Artifact (Anonymized, ≥0.85)\n                              │\n                              │  → Bridge validation\n                              │  → Gate: Consent, policy, compliance\n                              ▼\n                    ┌─────────────────┐\n                    │  KSP (MCP)      │  Trustless discovery via MCP\n                    │  - _discover    │  Tenant C → \"learn from Tenant A's healthcare patterns\"\n                    │  - _connect     │  KSP: \"here's the artifact\"\n                    │  - _broadcast   │  Tenant C → Twin: \"apply this pattern\"\n                    │  - _proxy       │  Twin: confidence evaluation, act\n                    └─────────────────┘\n                              │\n                              ▼\n                    Tenant B (Consumer) ──► Twin ──► Act\n```\n\n**KSP Interface:**\n\n```typescript\nexport interface KSPInterface {\n  _discover(query: KSPQuery): Promise<KSPArtifact[]>; // Trustless: no credential sharing\n  _connect(source: string, target: string): Promise<KSPConnection>;\n  _broadcast(artifact: KSPArtifact, tenants: string[]): Promise<KSPBroadcastResult>;\n  _proxy(artifact: KSPArtifact, via: string): Promise<KSPProxyResult>;\n}\n\nexport interface KSPArtifact {\n  id: string; // Artifact ID\n  origin: string; // Source tenant (or \"ecosystem\" for aggregate)\n  topology: string; // Cluster label\n  knowledgeType: \"best_practice\" | \"decision_pattern\" | \"workflow_template\" | \"insight\";\n  content: Record<string, unknown>; // Anonymized, structured\n  confidence: number; // ≥0.85 for distribution\n  version: string; // Semantic\n  createdAt: Date;\n  expiresAt: Date; // TTL for ephemeral knowledge\n}\n```\n\n### 9.3 Agent-to-Agent Communication (A2A)\n\nA2A is the **peer-to-peer messaging layer** for agents that share intent, not identity. Each agent has a unique AgentID (GUID). Communication is via JSON over HTTP/HTTPS or WebSockets, with JSON Schema validation.\n\n**A2A Protocol (5-step flow):**\n\n1. **Agent Declaration (A2A Step 1):** Agent A publishes its capabilities, goals, and trust to an AgentID broker.\n2. **Knowledge Exchange (A2A Step 2):** Agent A initiates a KSP connection with Agent B, exchanging cryptographically secure credentials.\n3. **Knowledge Flow (A2A Step 3):** Agent A sends a knowledge artifact to Agent B via KSP, which is validated and decrypted at the destination.\n4. **Action Execution (A2A Step 4):** Agent B interprets the artifact, executes the proposed action, and reports the outcome to the KSP.\n5. **Reward Mechanism (A2A Step 5):** Based on the outcome, the KSP updates the trust and reward levels of both agents.\n\n**A2A Message Schema:**\n\n```typescript\nexport interface A2AMessage {\n  fromAgentId: string; // GUID\n  toAgentId: string; // GUID (or \"*\" for broadcast)\n  type: \"intent\" | \"knowledge\" | \"action\" | \"reward\" | \"trust\";\n  payload: Record<string, unknown>;\n  signature: string; // Cryptographic signature\n  nonce: string; // Replay protection\n  timestamp: Date;\n  ttl: number; // TTL in seconds\n}\n```\n\n**A2A Boundaries:**\n\n- A2A NEVER crosses tenant boundaries. Agents are always scoped to a single tenant.\n- A2A is only for cross-service agent communication within the same tenant (e.g., `twin-orchestrator` → `knowledge` agent → `act` agent).\n- KSP (Knowledge Sharing Protocol) is the only cross-tenant communication channel. A2A is intra-tenant.\n\n### 9.4 Platform SDK (5 Variants)\n\nThe SDK is the consumer-facing bridge. It is 5 packages, 5 sizes, 5 use cases. The SDK abstracts the entire architecture: consumers never think about OODA, pipelines, or SSOC. They think about capabilities and entities.\n\n| Package            | Size    | Target                                          | API Coverage                                               | Key Capabilities                                                                                                             |\n| ------------------ | ------- | ----------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |\n| `@iw/sdk-full`     | ~180 KB | Workbench builders, extension devs              | Full (all capabilities, admin, auth, governance, identity) | `capability.invoke()`, `entity360.get()`, `memory.store()`, `twin.interact()`, `governance.listPending()`, `iam.provision()` |\n| `@iw/sdk-lite`     | ~85 KB  | Browser/Node apps, dashboards, CLI              | Standard (workbench, AI, memory, discovery, knowledge)     | `capability.invoke()`, `entity360.get()`, `memory.store()`, `knowledge.query()`, `discovery.search()`                        |\n| `@iw/sdk-content`  | ~45 KB  | Mobile, embedded widgets                        | Read-only (entity, memory, discovery, knowledge)           | `entity360.get()`, `knowledge.query()`, `discovery.search()`                                                                 |\n| `@iw/sdk-minimal`  | ~12 KB  | Analytics, telemetry, logging                   | Telemetry only                                             | `telemetry.emit()`, `metrics.report()`                                                                                       |\n| `@iw/sdk-headless` | ~3 KB   | Server-side, edge functions, background workers | Headless (no UI, no auth flow)                             | `capability.invoke()`, `token.refresh()`                                                                                     |\n\n**SDK Architecture:**\n\n```typescript\ninterface IWSdk {\n  capability: { invoke<T>(uri: string, payload?: unknown): Promise<CapabilityResponse<T>> };\n  entity360: { get(tenantId: string, entityId: string): Promise<Entity360> };\n  memory: {\n    store(tenantId: string, record: MemoryEntry): Promise<MemoryEntry>;\n    query(tenantId: string, q: MemoryQuery): Promise<MemoryEntry[]>;\n  };\n  twin: { interact(tenantId: string, userId: string, input: string): Promise<TwinResponse> };\n  knowledge: { query(tenantId: string, query: string): Promise<KnowledgeChunk[]> };\n  governance: {\n    listPending(tenantId: string, userId: string): Promise<PendingAction[]>;\n    resolve(tenantId: string, proposalId: string, resolution: \"approve\" | \"reject\"): Promise<void>;\n  };\n  discovery: { search(tenantId: string, query: string): Promise<DiscoveryResult[]> };\n  telemetry: { emit(event: TelemetryEvent): Promise<void> };\n  metrics: { report(metric: Metric): Promise<void> };\n  iam: { getUser(tenantId: string, userId: string): Promise<PlatformUser> };\n  auth: { getToken(): Promise<Token>; refreshToken(): Promise<Token> };\n  platform: { getHealth(): Promise<HealthStatus> };\n}\n```\n\n**SDK Authentication:** Every SDK instance carries a `tenantId` (from init) and a `token` (from auth). The token is refreshed automatically. The `tenantId` is injected into every capability invocation. This prevents the \"secret auth\" issue seen in v1.0.1: the token is NEVER stored as a plain string; it is managed by the SDK's internal auth manager.\n\n**SDK Usage Example (Workbench UI):**\n\n```typescript\n\nconst sdk = new IWSDK({\n  tenantId: \"t_abc123\",\n  authType: \"oidc\",\n  provider: \"auth0\",\n  clientId: \"my-app\",\n  redirectUri: \"https://app.integratewise.com/callback\",\n  scopes: [\"capability:read\", \"capability:write\", \"memory:personal\", \"twin:interact\"],\n});\n\n// Connect a new provider (triggers creamy sync)\nawait sdk.capability.invoke(\"platform.connect\", {\n  provider: \"salesforce\",\n  credentialType: \"nango\",\n  scopes: [\"api\", \"refresh_token\"],\n});\n\n// Get Entity360 for an account\nconst entity360 = await sdk.entity360.get(\"t_abc123\", \"ssoc_001\");\nconsole.log(entity360.primaryEntity); // { id, type, displayName, traits }\nconsole.log(entity360.relatedEntities); // 5 linked entities\nconsole.log(entity360.memory); // { lastInteraction, sentiment, keyTopics, openTasks }\n\n// Interact with the Twin\nconst twinResponse = await sdk.twin.interact(\n  \"t_abc123\",\n  \"u_456\",\n  \"What should I do about Acme Corp?\"\n);\nconsole.log(twinResponse.proposal); // { capability, confidence, payload, reasoning }\n\n// List pending proposals in My Desk\nconst pending = await sdk.governance.listPending(\"t_abc123\", \"u_456\");\npending.forEach((p) => console.log(p.summary, p.confidence, p.estimatedImpact));\n```\n\n### 9.5 CLI & Developer Tools\n\nThe CLI (`iw-cli`) is a terminal-based developer tool for advanced users, DevOps, and platform operators. It does not replace the SDK; it complements it for admin and debugging tasks.\n\n| CLI Command     | Purpose                                  | SDK Equivalent                                             |\n| --------------- | ---------------------------------------- | ---------------------------------------------------------- |\n| `iw auth`       | Login (device code flow, OAuth 2.0)      | `sdk.auth.getToken()`                                      |\n| `iw capability` | Invoke, discover, validate capabilities  | `sdk.capability.invoke()`, `sdk.capability.discover()`     |\n| `iw entity`     | Entity360 lookup, relationship graph     | `sdk.entity360.get()`                                      |\n| `iw memory`     | Query, store, reinforce memory           | `sdk.memory.query()`, `sdk.memory.store()`                 |\n| `iw twin`       | Interact with Twin, replay reasoning     | `sdk.twin.interact()`                                      |\n| `iw knowledge`  | Upload, query, index documents           | `sdk.knowledge.query()`                                    |\n| `iw governance` | Manage rules, review proposals           | `sdk.governance.listPending()`, `sdk.governance.setRule()` |\n| `iw sync`       | Trigger manual sync, inspect status      | `sdk.capability.invoke('platform.sync.*')`                 |\n| `iw iam`        | User CRUD, RBAC, SCIM provisioning       | `sdk.iam.getUser()`, `sdk.iam.provision()`                 |\n| `iw provider`   | Adapter health check, credential refresh | `sdk.capability.invoke('platform.provider.*')`             |\n| `iw telemetry`  | Export audit log, export telemetry       | `sdk.telemetry.emit()`                                     |\n| `iw config`     | View/edit tenant config (admin only)     | N/A (admin)                                                |\n\n**CLI Architecture:** The CLI is a Node.js app built on `oclif`. It wraps the `@iw/sdk-headless` package. Every command authenticates via the SDK's auth manager. No credentials are stored in the CLI config.\n\n---\n\n## 10. Data Model & Entity Relationships\n\n> _\"The Data Model is the spine of the Adaptive Spine. It is the contract that every service, every adapter, every pipeline must honor.\"_\n\nThis section defines the canonical data structures, entity relationships, and database schemas that power the IntegrateWise Continuity Bridge. All definitions use Drizzle ORM with SQLite (D1) dialect. KV cache and R2 storage are used for performance and archival, but D1 is the single source of truth for all relational data.\n\n### 10.1 Spine (Entities, Relationships, Audit)\n\nThe Spine is the **directed property graph** that stores all canonical entities, their relationships, and the audit trail. Every table is prefixed with `spine_` for clarity.\n\n```typescript\n// ============================================\n// SPINE — ENTITIES\n// ============================================\n  id: text(\"id\").primaryKey(), // SSOC UUID (v4, stable)\n  tenantId: text(\"tenant_id\").notNull(),\n  entityType: text(\"entity_type\").notNull(), // 'account', 'contact', 'deal', 'task', 'email', 'meeting', 'note'\n  sourceId: text(\"source_id\").notNull(), // Provider-native ID\n  sourceProvider: text(\"source_provider\").notNull(), // 'salesforce', 'hubspot', 'slack', 'github', 'stripe'\n  canonicalName: text(\"canonical_name\"),\n  traits: text(\"traits\", { mode: \"json\" }).$defaultFn(() => \"{}\"), // Normalized key-value\n  ssocConfidence: real(\"ssoc_confidence\").$defaultFn(() => 1.0), // 0.0–1.0\n  firstSeenAt: integer(\"first_seen_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  lastModifiedAt: integer(\"last_modified_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  archivedAt: integer(\"archived_at\", { mode: \"timestamp\" }),\n  // Indexes: (tenant_id, entity_type), (tenant_id, source_provider, source_id) UNIQUE\n});\n\n  id: text(\"id\").primaryKey(),\n  entityId: text(\"entity_id\")\n    .notNull()\n    .references(() => spineEntities.id),\n  tenantId: text(\"tenant_id\").notNull(),\n  key: text(\"key\").notNull(), // e.g., 'email', 'phone', 'status'\n  value: text(\"value\"),\n  valueType: text(\"value_type\", {\n    enum: [\"string\", \"number\", \"boolean\", \"date\", \"json\"],\n  }).notNull(),\n  confidence: real(\"confidence\").$defaultFn(() => 1.0),\n  source: text(\"source\"), // 'sync', 'user', 'twin', 'inferred'\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, entity_id, key)\n});\n\n// ============================================\n// SPINE — RELATIONSHIPS\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  sourceEntityId: text(\"source_entity_id\")\n    .notNull()\n    .references(() => spineEntities.id),\n  targetEntityId: text(\"target_entity_id\")\n    .notNull()\n    .references(() => spineEntities.id),\n  relationType: text(\"relation_type\").notNull(), // 'owns', 'reports_to', 'participates_in', 'attended', 'sent', 'linked_to'\n  confidence: real(\"confidence\").notNull(),\n  provenance: text(\"provenance\"), // 'inferred', 'declared', 'synced', 'user_created'\n  firstInferredAt: integer(\"first_inferred_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, source_entity_id), (tenant_id, target_entity_id)\n});\n\n// ============================================\n// SPINE — AUDIT LOG\n// ============================================\n  id: text(\"id\").primaryKey(), // ULID\n  tenantId: text(\"tenant_id\").notNull(),\n  entityId: text(\"entity_id\"), // SSOC UUID or null for non-entity actions\n  action: text(\"action\").notNull(), // 'create', 'update', 'delete', 'read', 'mcp_invoke', 'credential_access', 'sync', 'sync_error'\n  actor: text(\"actor\").notNull(), // 'user:<id>', 'twin', 'agent:<id>', 'system', 'service:<name>'\n  via: text(\"via\"), // 'mcp', 'webhook', 'sync', 'act', 'govern', 'twin', 'normalizer', 'pipeline'\n  payloadHash: text(\"payload_hash\"), // SHA-256 of normalized payload\n  metadata: text(\"metadata\", { mode: \"json\" }),\n  timestamp: integer(\"timestamp\", { mode: \"timestamp\" })\n    .notNull()\n    .$defaultFn(() => new Date()),\n  // Index: (tenant_id, timestamp), (tenant_id, entity_id, timestamp)\n});\n```\n\n### 10.2 Memory Schema (4 Scopes, 3 Tiers)\n\n```typescript\n// ============================================\n// MEMORY — RECORDS\n// ============================================\n  id: text(\"id\").primaryKey(), // ULID\n  tenantId: text(\"tenant_id\").notNull(),\n  scope: text(\"scope\", { enum: [\"personal\", \"work\", \"organization\", \"ai\"] }).notNull(),\n  ownerId: text(\"owner_id\").notNull(), // user_id for personal, task_id for work, tenant_id for org, agent_id for ai\n  tier: text(\"tier\", { enum: [\"active\", \"staging\", \"archived\"] }).notNull(),\n  content: text(\"content\").notNull(),\n  embedding: text(\"embedding\"), // Serialized vector (optional, also in Vectorize)\n  confidence: real(\"confidence\").notNull(), // Triage-assigned confidence\n  source: text(\"source\").notNull(), // 'sync', 'user_action', 'twin_proposal', 'act_writeback', 'twin_observation', 'external'\n  ssocReferences: text(\"ssoc_references\", { mode: \"json\" }).$defaultFn(() => \"[]\"), // Array of SSOC IDs\n  accessCount: integer(\"access_count\").$defaultFn(() => 0),\n  lastAccessedAt: integer(\"last_accessed_at\", { mode: \"timestamp\" }),\n  decayAt: integer(\"decay_at\", { mode: \"timestamp\" }), // When auto-archive\n  validUntil: integer(\"valid_until\", { mode: \"timestamp\" }), // null = permanent\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  updatedAt: integer(\"updated_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, scope, owner_id, tier), (tenant_id, decay_at)\n});\n\n// ============================================\n// MEMORY — DECAY LOG\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  memoryId: text(\"memory_id\")\n    .notNull()\n    .references(() => memoryRecords.id),\n  fromTier: text(\"from_tier\", { enum: [\"active\", \"staging\", \"archived\"] }).notNull(),\n  toTier: text(\"to_tier\", { enum: [\"active\", \"staging\", \"archived\", \"discarded\"] }).notNull(),\n  decayScore: real(\"decay_score\").notNull(),\n  reason: text(\"reason\").notNull(), // 'time_decay', 'reinforcement', 'manual_archive', 'triage_rule'\n  executedAt: integer(\"executed_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, memory_id, executed_at)\n});\n\n// ============================================\n// MEMORY — REINFORCEMENT LOG\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  memoryId: text(\"memory_id\")\n    .notNull()\n    .references(() => memoryRecords.id),\n  triggeredBy: text(\"triggered_by\").notNull(), // 'user_access', 'twin_reference', 'act_writeback', 'manual'\n  oldAccessCount: integer(\"old_access_count\").notNull(),\n  newAccessCount: integer(\"new_access_count\").notNull(),\n  oldDecayAt: integer(\"old_decay_at\", { mode: \"timestamp\" }),\n  newDecayAt: integer(\"new_decay_at\", { mode: \"timestamp\" }),\n  reinforcedAt: integer(\"reinforced_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, memory_id, reinforced_at)\n});\n```\n\n### 10.3 Tenant Schema (Workspace, Users, RBAC, Billing)\n\n```typescript\n// ============================================\n// TENANT — WORKSPACE\n// ============================================\n  id: text(\"id\").primaryKey(), // tenant_id\n  name: text(\"name\").notNull(),\n  slug: text(\"slug\").notNull().unique(),\n  plan: text(\"plan\", { enum: [\"free\", \"starter\", \"growth\", \"enterprise\"] }).$defaultFn(\n    () => \"free\"\n  ),\n  status: text(\"status\", { enum: [\"active\", \"suspended\", \"cancelled\"] }).$defaultFn(() => \"active\"),\n  dataResidency: text(\"data_residency\", { enum: [\"global\", \"eu\", \"us\", \"au\"] }).$defaultFn(\n    () => \"global\"\n  ),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  updatedAt: integer(\"updated_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n});\n\n// ============================================\n// TENANT — USERS\n// ============================================\n  id: text(\"id\").primaryKey(), // user_id\n  tenantId: text(\"tenant_id\")\n    .notNull()\n    .references(() => workspaces.id),\n  email: text(\"email\").notNull(),\n  displayName: text(\"display_name\"),\n  avatarUrl: text(\"avatar_url\"),\n  role: text(\"role\", {\n    enum: [\"owner\", \"tam\", \"account_success\", \"admin\", \"member\", \"viewer\"],\n  }).notNull(),\n  isActive: integer(\"is_active\", { mode: \"boolean\" }).$defaultFn(() => true),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  lastLoginAt: integer(\"last_login_at\", { mode: \"timestamp\" }),\n  // Index: (tenant_id, email), (tenant_id, role)\n});\n\n// ============================================\n// TENANT — RBAC\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\")\n    .notNull()\n    .references(() => workspaces.id),\n  name: text(\"name\").notNull(), // 'owner', 'admin', 'member', 'viewer'\n  permissions: text(\"permissions\", { mode: \"json\" }).notNull(), // JSON array of capability URIs\n  isSystem: integer(\"is_system\", { mode: \"boolean\" }).$defaultFn(() => false),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, name)\n});\n\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  userId: text(\"user_id\")\n    .notNull()\n    .references(() => platformUsers.id),\n  roleId: text(\"role_id\")\n    .notNull()\n    .references(() => tenantRoles.id),\n  assignedBy: text(\"assigned_by\")\n    .notNull()\n    .references(() => platformUsers.id),\n  assignedAt: integer(\"assigned_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, user_id), (tenant_id, role_id)\n});\n\n// ============================================\n// TENANT — BILLING\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\")\n    .notNull()\n    .references(() => workspaces.id),\n  stripeSubscriptionId: text(\"stripe_subscription_id\").unique(),\n  plan: text(\"plan\").notNull(),\n  status: text(\"status\", {\n    enum: [\"active\", \"trialing\", \"past_due\", \"cancelled\", \"paused\"],\n  }).notNull(),\n  currentPeriodStart: integer(\"current_period_start\", { mode: \"timestamp\" }),\n  currentPeriodEnd: integer(\"current_period_end\", { mode: \"timestamp\" }),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  updatedAt: integer(\"updated_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n});\n```\n\n### 10.4 Governance Schema (Proposals, Audit, HITL)\n\n```typescript\n// ============================================\n// GOVERNANCE — PROPOSALS\n// ============================================\n  id: text(\"id\").primaryKey(), // ULID\n  tenantId: text(\"tenant_id\").notNull(),\n  userId: text(\"user_id\").notNull(), // Target user for approval\n  capability: text(\"capability\").notNull(), // Capability URI\n  confidence: real(\"confidence\").notNull(),\n  status: text(\"status\", {\n    enum: [\"pending\", \"approved\", \"rejected\", \"modified\", \"discarded\", \"expired\", \"auto_approved\"],\n  }).notNull(),\n  payload: text(\"payload\", { mode: \"json\" }), // Normalized payload\n  summary: text(\"summary\").notNull(), // Human-readable summary\n  reasoningTrace: text(\"reasoning_trace\"), // Twin reasoning\n  estimatedImpact: text(\"estimated_impact\", { mode: \"json\" }), // { revenue, timeSaved, riskScore }\n  approvedBy: text(\"approved_by\"), // user_id or null\n  approvedAt: integer(\"approved_at\", { mode: \"timestamp\" }),\n  modifiedBy: text(\"modified_by\"),\n  modifiedAt: integer(\"modified_at\", { mode: \"timestamp\" }),\n  discardedAt: integer(\"discarded_at\", { mode: \"timestamp\" }),\n  expiresAt: integer(\"expires_at\", { mode: \"timestamp\" }), // 72h from creation\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  // Index: (tenant_id, user_id, status), (tenant_id, expires_at)\n});\n\n// ============================================\n// GOVERNANCE — AUDIT LOG\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  proposalId: text(\"proposal_id\")\n    .notNull()\n    .references(() => actionProposals.id),\n  action: text(\"action\").notNull(), // 'auto_approved', 'queued', 'discarded', 'approved', 'rejected', 'modified', 'expired', 'rule_blocked', 'escalated'\n  actor: text(\"actor\").notNull(), // 'user:<id>', 'twin', 'system', 'rule:<id>'\n  reason: text(\"reason\").notNull(),\n  metadata: text(\"metadata\", { mode: \"json\" }),\n  timestamp: integer(\"timestamp\", { mode: \"timestamp\" })\n    .notNull()\n    .$defaultFn(() => new Date()),\n  // Index: (tenant_id, proposal_id, timestamp)\n});\n\n// ============================================\n// GOVERNANCE — HITL SESSIONS\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId: text(\"tenant_id\").notNull(),\n  userId: text(\"user_id\").notNull(),\n  proposalId: text(\"proposal_id\")\n    .notNull()\n    .references(() => actionProposals.id),\n  status: text(\"status\", { enum: [\"open\", \"resolved\", \"expired\"] }).notNull(),\n  openedAt: integer(\"opened_at\", { mode: \"timestamp\" }).$defaultFn(() => new Date()),\n  resolvedAt: integer(\"resolved_at\", { mode: \"timestamp\" }),\n  resolution: text(\"resolution\", { enum: [\"approve\", \"reject\", \"modify\", \"dismiss\"] }),\n  // Index: (tenant_id, user_id, status), (tenant_id, opened_at)\n});\n```\n\n### 10.5 Entity Relationship Diagram (ERD)\n\n```\n                    ┌─────────────────────┐\n                    │     WORKSPACE       │\n                    │   (tenant_id = PK)  │\n                    │                     │\n                    │  1:N  ──────────────┼────► PLATFORM_USERS\n                    │  1:N  ──────────────┼────► TENANT_ROLES\n                    │  1:N  ──────────────┼────► BILLING_SUBSCRIPTIONS\n                    │  1:N  ──────────────┼────► CONNECTORS\n                    │  1:N  ──────────────┼────► KNOWLEDGE_DOCUMENTS\n                    │  1:1  ──────────────┼────► TELEMETRY_HUB_CONFIG\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │   PLATFORM_USERS    │\n                    │  (user_id = PK)     │\n                    │                     │\n                    │  N:1  ──────────────┼────► TENANT_ROLES (via ROLE_ASSIGNMENTS)\n                    │  1:1  ──────────────┼────► HITL_SESSIONS (active)\n                    │  1:N  ──────────────┼────► ACTION_PROPOSALS (as reviewer)\n                    │  1:N  ──────────────┼────► SPINE_AUDIT_LOG (as actor)\n                    │  1:N  ──────────────┼────► MEMORY_RECORDS (personal scope)\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │    SPINE_ENTITIES   │\n                    │   (id = SSOC = PK)  │\n                    │                     │\n                    │  1:N  ──────────────┼────► SPINE_ENTITY_TRAITS\n                    │  1:N  ──────────────┼────► SPINE_RELATIONSHIPS (source)\n                    │  1:N  ──────────────┼────► SPINE_RELATIONSHIPS (target)\n                    │  1:N  ──────────────┼────► SPINE_AUDIT_LOG\n                    │  1:N  ──────────────┼────► MEMORY_RECORDS (via ssocReferences)\n                    │  1:N  ──────────────┼────► ACTION_PROPOSALS (affected entity)\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │ SPINE_RELATIONSHIPS │\n                    │  (id = PK)          │\n                    │  source_entity_id   │\n                    │  target_entity_id   │\n                    │  relation_type      │\n                    │  confidence         │\n                    │  provenance         │\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │   MEMORY_RECORDS    │\n                    │   (id = PK)         │\n                    │   scope             │\n                    │   tier              │\n                    │   confidence        │\n                    │  1:N  ──────────────┼────► MEMORY_DECAY_LOG\n                    │  1:N  ──────────────┼────► MEMORY_REINFORCEMENT_LOG\n                    │  1:N  ──────────────┼────► SPINE_AUDIT_LOG (as resource)\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │  ACTION_PROPOSALS   │\n                    │   (id = PK)         │\n                    │   capability        │\n                    │   confidence        │\n                    │   status            │\n                    │  1:N  ──────────────┼────► GOVERNANCE_AUDIT_LOG\n                    │  1:1  ──────────────┼────► HITL_SESSIONS\n                    └─────────────────────┘\n                              │\n                              │ 1:N\n                              ▼\n                    ┌─────────────────────┐\n                    │ GOVERNANCE_AUDIT_LOG│\n                    │   (id = PK)         │\n                    │   action            │\n                    │   actor               │\n                    │   reason            │\n                    │   timestamp         │\n                    └─────────────────────┘\n```\n\n**ERD Constraints (CRITICAL):**\n\n1. All tables have `tenant_id` as first column in every composite index.\n2. `spine_entities.id` is a UUIDv4 (SSOC) — never provider-native ID.\n3. `spine_relationships` has bidirectional integrity: deleting a source entity must cascade delete relationships.\n4. `action_proposals` has `expiresAt` — auto-expiry after 72 hours (enforced by HITL DO alarm).\n5. `memory_records` has `decayAt` — used by nightly Triage Worker for tier promotion.\n6. `spine_audit_log` is append-only — never UPDATE, never DELETE. No cascade delete from any table to audit log.\n\n### 10.6 Drizzle ORM Definitions (Full Snippet)\n\n```typescript\n// ============================================\n// SHARED SCHEMA COMPOSITION\n// ============================================\n  integer(name, { mode: \"timestamp\" }).$defaultFn(() => new Date());\n\n// ============================================\n// CAPABILITY REGISTRY (§4.1)\n// ============================================\n  id: text(\"id\").primaryKey(), // \"salesforce.opportunity.update\"\n  tenantId,\n  namespace: text(\"namespace\").notNull(),\n  name: text(\"name\").notNull(),\n  displayName: text(\"display_name\"),\n  description: text(\"description\"),\n  version: text(\"version\").notNull().default(\"1.0.0\"),\n  oodaPhase: text(\"ooda_phase\", {\n    enum: [\"observe\", \"orient\", \"decide\", \"act\", \"cross-phase\"],\n  }).notNull(),\n  runtime: text(\"runtime\", { enum: [\"agent\", \"pipeline\", \"workflow\", \"mcp\", \"direct\"] }).notNull(),\n  serviceBinding: text(\"service_binding\").notNull(),\n  handler: text(\"handler\").notNull(),\n  inputSchema: text(\"input_schema\"),\n  outputSchema: text(\"output_schema\"),\n  requiresApproval: integer(\"requires_approval\", { mode: \"boolean\" }).default(false),\n  minConfidence: real(\"min_confidence\").default(0.7),\n  autoApproveThreshold: real(\"auto_approve_threshold\").default(0.85),\n  requiredRoles: text(\"required_roles\"),\n  requiredScopes: text(\"required_scopes\"),\n  isActive: integer(\"is_active\", { mode: \"boolean\" }).default(true),\n  isDeprecated: integer(\"is_deprecated\", { mode: \"boolean\" }).default(false),\n  deprecationNote: text(\"deprecation_note\"),\n  createdAt,\n  updatedAt,\n});\n\n// ============================================\n// KNOWLEDGE (§5.7)\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  title: text(\"title\").notNull(),\n  source: text(\"source\", { enum: [\"upload\", \"sync\", \"twin_generated\", \"user_created\"] }).notNull(),\n  mimeType: text(\"mime_type\").notNull(),\n  r2Key: text(\"r2_key\").notNull(),\n  chunks: text(\"chunks\", { mode: \"json\" }).$defaultFn(() => \"[]\"),\n  metadata: text(\"metadata\", { mode: \"json\" }).$defaultFn(() => \"{}\"),\n  createdBy: text(\"created_by\").notNull(),\n  createdAt,\n  updatedAt,\n});\n\n  id: text(\"id\").primaryKey(),\n  documentId: text(\"document_id\")\n    .notNull()\n    .references(() => knowledgeDocuments.id),\n  tenantId,\n  content: text(\"content\").notNull(),\n  embedding: text(\"embedding\"),\n  startIndex: integer(\"start_index\").notNull(),\n  endIndex: integer(\"end_index\").notNull(),\n  metadata: text(\"metadata\", { mode: \"json\" }).$defaultFn(() => \"{}\"),\n  createdAt,\n});\n\n// ============================================\n// CONNECTORS (§7.1)\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  provider: text(\"provider\").notNull(),\n  name: text(\"name\").notNull(),\n  credentialType: text(\"credential_type\", { enum: [\"nango\", \"native\", \"mcp\"] }).notNull(),\n  status: text(\"status\", { enum: [\"connected\", \"disconnected\", \"error\", \"syncing\"] }).notNull(),\n  lastSyncAt: integer(\"last_sync_at\", { mode: \"timestamp\" }),\n  nextSyncAt: integer(\"next_sync_at\", { mode: \"timestamp\" }),\n  syncError: text(\"sync_error\"),\n  createdAt,\n  updatedAt,\n});\n\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  connectorId: text(\"connector_id\")\n    .notNull()\n    .references(() => connectors.id),\n  resource: text(\"resource\").notNull(),\n  providerSchema: text(\"provider_schema\", { mode: \"json\" }).notNull(),\n  canonicalType: text(\"canonical_type\").notNull(),\n  mappingRules: text(\"mapping_rules\", { mode: \"json\" }).notNull(),\n  createdAt,\n  updatedAt,\n});\n\n// ============================================\n// SCHEMA PROVIDER (§5.4)\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  entityType: text(\"entity_type\").notNull(),\n  version: text(\"version\").notNull().default(\"1.0.0\"),\n  baseSchema: text(\"base_schema\", { mode: \"json\" }).notNull(), // IntegrateWise IP\n  tenantOverlay: text(\"tenant_overlay\", { mode: \"json\" }).$defaultFn(() => \"{}\"), // Customer context\n  providerMap: text(\"provider_map\", { mode: \"json\" }).$defaultFn(() => \"{}\"), // Adapter config\n  isActive: integer(\"is_active\", { mode: \"boolean\" }).default(true),\n  createdAt,\n  updatedAt,\n});\n\n// ============================================\n// TELEMETRY & METRICS\n// ============================================\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  userId: text(\"user_id\"),\n  sessionId: text(\"session_id\"),\n  eventType: text(\"event_type\").notNull(),\n  eventData: text(\"event_data\", { mode: \"json\" }).$defaultFn(() => \"{}\"),\n  timestamp: integer(\"timestamp\", { mode: \"timestamp\" })\n    .notNull()\n    .$defaultFn(() => new Date()),\n});\n\n  id: text(\"id\").primaryKey(),\n  tenantId,\n  metricName: text(\"metric_name\").notNull(),\n  value: real(\"value\").notNull(),\n  labels: text(\"labels\", { mode: \"json\" }).$defaultFn(() => \"{}\"),\n  timestamp: integer(\"timestamp\", { mode: \"timestamp\" })\n    .notNull()\n    .$defaultFn(() => new Date()),\n});\n```\n\n---\n\n## 11. Security Model & Zero-Trust Architecture\n\n> _\"Security is not a layer. It is a property. Every line of code, every API call, every Durable Object activation must be tenant-isolated, credential-safe, and auditable.\"_\n\nThe security model is a **defense-in-depth, zero-trust, tenant-isolated** system. Every service, every Durable Object, every adapter, every queue message must satisfy the security invariants. Failures are **fail-loud** — never fail-silent.\n\n### 11.1 4-Zone Trust Model\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                              PUBLIC ZONE                                    │\n│  Ingress (Cloudflare + WAF) → SDK → OAuth 2.0 / OIDC / Device Code Flow   │\n│  No credentials, no provider secrets, no tenant data                         │\n└─────────────────────────────────────────────────────────────────────────────┘\n                                      │\n                                      ▼ (WAF validated, JWT issued)\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                              CONSUMER ZONE                                  │\n│  Workbench → SDK → Gateway → API-1 (JWT + RBAC + Rate Limit)               │\n│  Tenant-scoped capabilities, read-only by default, write requires scope       │\n│  Validation: JWT signature, tenant_id match, role match, scope match         │\n└─────────────────────────────────────────────────────────────────────────────┘\n                                      │\n                                      ▼ (Service Binding, no JWT re-validation)\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                              SERVICE ZONE                                   │\n│  Capability Layer → Continuity Layer → Governance Layer → Provider Fabric    │\n│  Service bindings: no credentials shared, no Durable Objects reused         │\n│  Internal headers: `x-tenant-id`, `x-correlation-id`, `x-caller-service`     │\n│  Access: D1 (read-only except pipeline), KV (read-only except pipeline)   │\n└─────────────────────────────────────────────────────────────────────────────┘\n                                      │\n                                      ▼ (Credential Wall)\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                              PROVIDER ZONE                                  │\n│  Nango / MCP / Native adapters → External APIs                              │\n│  All credentials stored in Cloudflare Secrets or Nango Vault                │\n│  NEVER logged. NEVER returned. Accessed via Credential Wall.                │\n│  Every call: Tenant ID, Correlation ID, Entity360 Context, Audit Log        │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 11.2 Core Security Principles (P0)\n\n| ID       | Principle                      | Implementation                                                                                                                     | Failure Behavior                                                     |\n| -------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |\n| **P0-1** | **Zero-Trust Everywhere**      | Every request is authenticated, authorized, and audited. No internal exceptions.                                                   | Unauthorized: 403. Invalid JWT: 401. Missing scope: 403.             |\n| **P0-2** | **Tenant Isolation by Design** | `WHERE tenant_id = ?` on every query. Durable Object IDs include `tenant_id`. Every queue message carries `tenant_id`.             | Cross-tenant query: 403 + alert. Shared DO: crash.                   |\n| **P0-3** | **Credential Separation**      | `pipeline` is the only writer to D1. Adapters NEVER access credentials directly. Secrets are in Cloudflare Secrets or Nango Vault. | Credential leak: immediate rotation. Invalid credential access: 403. |\n| **P0-4** | **Durable Object Security**    | `DO ID = idFromName(tenant_id + \":\" + user_id)`. NEVER `idFromName(\"global\")`. NEVER share DOs.                                    | Shared DO: crash. Missing tenant prefix: 403.                        |\n| **P0-5** | **Fail-Loud**                  | All cross-tenant detection, credential leakage, and misconfigurations trigger immediate errors + audit.                            | No fail-silent. Every P0 failure is logged + alerted.                |\n| **P0-6** | **Idempotency by Design**      | Every write operation carries an idempotency key. Replay of the same key returns the same result.                                  | Duplicate execution: 409 + cached result.                            |\n| **P0-7** | **Least Privilege**            | Every service, every adapter, every Durable Object has the minimum permissions needed.                                             | Over-permission: detected by security review, blocked.               |\n| **P0-8** | **Encryption by Default**      | At-rest: D1 encrypted by Cloudflare. In-transit: TLS 1.3. Credentials: Cloudflare Secrets.                                         | Unencrypted transit: rejected. Unencrypted at-rest: flagged.         |\n| **P0-9** | **MCP Security**               | MCP sessions: TLS 1.3 + OAuth 2.0. Outbound MCP: `_iw_context` injection. Read-only default.                                       | Unauthorized MCP: 403. Write without scope: 403.                     |\n\n### 11.3 Provider Fabric Security (P1)\n\n| ID       | Principle                   | Implementation                                                                     | Failure Behavior                                       |\n| -------- | --------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------ |\n| **P1-1** | **Credential Wall**         | `getCredentials()` is the ONLY function that fetches secrets. NEVER direct access. | Direct credential access: rejected + audit.            |\n| **P1-2** | **Credential Rotation**     | Nango auto-refresh. Native: admin CLI or cron. Max age: 90 days.                   | Expired credential: rejected. Stale credential: alert. |\n| **P1-3** | **Cross-Tenant Prevention** | Credential lookup is scoped to `tenant_id`. No provider-level shared credentials.  | Cross-tenant credential: 403 + alert.                  |\n| **P1-4** | **Audit Provider Calls**    | Every provider call logs to `outbound_mcp_calls` + `spine_audit_log`.              | Missing audit: operation halted.                       |\n| **P1-5** | **Provider Call Policy**    | Scope-based: `read-only` (default), `write-with-governance`, `full-access`.        | Scope violation: 403.                                  |\n| **P1-6** | **Credential Refresh**      | Native: 60 min. MCP: 5 min. Nango: handled by Nango.                               | Stale token: rejected. Refresh failure: alert.         |\n| **P1-7** | **Backup & Recovery**       | All secrets: encrypted, backed up to offsite.                                      | Loss: recovery from backup.                            |\n| **P1-8** | **Credential Monitoring**   | Anomaly detection: unusual call volume, unexpected IPs.                            | Anomaly: alert + circuit breaker.                      |\n| **P1-9** | **MCP Security Boundaries** | TLS 1.3, OAuth 2.0, `_iw_context` injection, read-only default, scope-based write. | Violation: 403.                                        |\n\n### 11.4 Compliance Framework (SOC 2, GDPR, HIPAA)\n\n| Standard          | Controls                                                                      | Implementation                                                                                                                         |\n| ----------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **SOC 2 Type II** | Access controls, encryption, monitoring, incident response, change management | RBAC, D1 encryption, audit logs, alerting, CI/CD pipeline with approval gates                                                          |\n| **GDPR**          | Data subject rights, lawful basis, minimization, breach notification          | Right to Erasure (anonymize audit subjects), Right to Access (export API), Right to Portability (export JSON), 72h breach notification |\n| **HIPAA**         | PHI access, audit, encryption, business associate agreements                  | HIPAA-eligible on enterprise plan, BAA with Cloudflare, PHI in encrypted fields, audit trail for 7 years                               |\n| **CCPA**          | Right to know, delete, opt-out                                                | Data inventory, deletion API, opt-out for telemetry sharing                                                                            |\n\n**Data Residency:**\n\n- Free/Starter: Global Cloudflare edge\n- Growth: EU or US region selectable\n- Enterprise: Custom region + jurisdiction pinning + data processing agreement\n\n**Data Retention:**\n| Data Type | Active | Archive | Retention |\n|-----------|--------|---------|-----------|\n| Memory (active) | D1 + KV | R2 | 30 days (with reinforcement) |\n| Memory (permanent) | D1 + KV | — | Permanent (until workspace deletion) |\n| Audit log | D1 | R2 | 7 years |\n| Telemetry | KV | R2 | 7 days |\n| Provider call log | D1 | R2 | 90 days |\n| Normalizer DLQ | R2 | — | 7 years |\n| Governance proposals | D1 | R2 | 7 years |\n\n### 11.5 Threat Model (Key Threats & Mitigations)\n\n| Threat                       | Vector                                               | Impact        | Mitigation                                                                                         |\n| ---------------------------- | ---------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- |\n| **Cross-Tenant Data Access** | Bug in query logic, missing `WHERE tenant_id`        | CRITICAL (P0) | Strict query builder, DO ID prefix, service binding propagation, automated tests, fail-loud        |\n| **Credential Leakage**       | Log exposure, adapter misconfig, memory dump         | CRITICAL (P0) | Cloudflare Secrets, no credential in logs, Credential Wall, Nango Vault, rotation                  |\n| **DoS / Abuse**              | Rate limit bypass, credential stuffing, webhook spam | HIGH (P1)     | WAF, API-1 rate limits, queue backpressure, circuit breaker, KV-based DDoS protection              |\n| **LLM Injection**            | Malicious prompt via MCP or workbench                | HIGH (P1)     | Input validation, output sanitization, structured prompts, capability scoping, no raw SQL from LLM |\n| **Data Poisoning**           | Malformed provider data corrupts Spine               | HIGH (P1)     | Schema validation, Normalizer NA6 stage, 8-stage pipeline, reject non-conforming data              |\n| **Supply Chain Attack**      | Malicious adapter, compromised dependency            | HIGH (P1)     | Lockfile review, SCA, dependency pinning, container signing, reproducible builds                   |\n| **Insider Threat**           | Admin override, credential abuse                     | MEDIUM (P2)   | Audit logs, approval gates, principle of least privilege, admin activity monitoring                |\n| **Provider Compromise**      | Provider breach, API key leak                        | MEDIUM (P2)   | Credential rotation, least-privilege scopes, anomaly detection, incident response plan             |\n| **Replay Attack**            | Reuse of old request payload                         | MEDIUM (P2)   | Idempotency keys, JWT exp, nonce, timestamp validation                                             |\n| **Ecosystem Breach**         | Cross-tenant knowledge sharing leak                  | MEDIUM (P2)   | Anonymization, confidence threshold ≥0.85, KSP consent, policy compliance                          |\n| **DDoS on Workers**          | Burst traffic overwhelms Workers                     | MEDIUM (P2)   | KV-based rate limiting, queue backpressure, auto-scaling, WAF                                      |\n\n### 11.6 Authentication & Authorization\n\n**OAuth 2.0 / OIDC Flow:**\n\n```\nUser ──► Workbench ──► Auth0 / Cognito / Keycloak ──► JWT Token\n                               │\n                               │  id_token (JWT) + access_token (opaque)\n                               │\n                               ▼\n                         ┌─────────────────┐\n                         │  JWT Claims     │\n                         │  sub: user_id   │\n                         │  tenant_id: t_  │\n                         │  rbac: role     │\n                         │  scope: array   │\n                         │  exp: 3600s     │\n                         └─────────────────┘\n                               │\n                               ▼\n                         Gateway (API-1)\n                         - Validate signature\n                         - Extract tenant_id\n                         - Verify role + scope\n                         - Inject x-tenant-id header\n```\n\n**Device Code Flow (CLI):**\n\n1. `iw auth` → SDK requests device code from Auth0\n2. User opens browser, authenticates\n3. CLI polls for token\n4. Token received, stored in keychain (never plain file)\n\n**RBAC Roles:**\n\n| Role                | Read | Write | Admin | Governance | Twin | AI  |\n| ------------------- | ---- | ----- | ----- | ---------- | ---- | --- |\n| **owner**           | All  | All   | All   | All        | All  | All |\n| **tam**             | All  | All   | No    | All        | All  | All |\n| **account_success** | All  | All   | No    | No         | All  | All |\n| **admin**           | All  | All   | All   | All        | All  | All |\n| **member**          | Own  | Own   | No    | No         | Own  | Own |\n| **viewer**          | Read | No    | No    | No         | No   | No  |\n\n### 11.7 Audit & Compliance Logging\n\nAll audit logs are **immutable, append-only, fail-loud**. See §6.4 for the `AuditEvent` interface and `auditLog()` function.\n\n**Audit Log Retention:**\n\n- D1: 90 days (hot queries)\n- R2: 7 years (cold storage, compliance)\n- KV: NOT used for audit (ephemeral, not persistent)\n\n**Audit Log Access:**\n\n- Read: `govern` service, `iam` service (admin), `audit` CLI command\n- Export: API endpoint `/v1/audit/export` (JSON, CSV, Parquet)\n- Deletion: NEVER. GDPR anonymization: update `dataSubjectId` to `anonymized` hash, keep audit trail.\n\n### 11.8 Secret Management\n\n| Tier                 | Storage            | Access                    | Rotation           | Backup       |\n| -------------------- | ------------------ | ------------------------- | ------------------ | ------------ |\n| **OAuth tokens**     | Nango Vault        | `act` via Credential Wall | Nango auto-refresh | Nango backup |\n| **API keys**         | Cloudflare Secrets | `act` via Credential Wall | Admin CLI, 90 days | CF backup    |\n| **JWT signing keys** | Cloudflare Secrets | `gateway`                 | Admin CLI, 90 days | CF backup    |\n| **DB credentials**   | Cloudflare Secrets | `pipeline`                | Admin CLI, 90 days | CF backup    |\n| **Encryption keys**  | Cloudflare Secrets | `pipeline`                | Admin CLI, 1 year  | CF backup    |\n| **MCP tokens**       | Cloudflare Secrets | `act` via Credential Wall | Admin CLI, 90 days | CF backup    |\n\n**Secret Rotation Process:**\n\n1. Generate new secret\n2. Update Cloudflare Secrets\n3. Deploy with new secret reference\n4. Grace period: 24h (both old and new valid)\n5. Invalidate old secret\n6. Audit log: `secret_rotated`\n\n### 11.9 Data Residency & Encryption\n\n**Encryption:**\n\n- At-rest: D1 encrypted by Cloudflare (AES-256). R2 encrypted by Cloudflare (AES-256).\n- In-transit: TLS 1.3 (minimum).\n- In-memory: Cloudflare Workers memory is ephemeral, no persistent encryption needed.\n- Credentials: Cloudflare Secrets (AES-256).\n\n**Data Residency:**\n| Plan | Default | Configurable | Jurisdiction |\n|------|---------|-------------|-------------|\n| Free | Global | No | — |\n| Starter | Global | No | — |\n| Growth | Global | Yes (EU, US) | GDPR, CCPA |\n| Enterprise | Custom | Yes (EU, US, AU, JP) | Custom DPA |\n\n### 11.10 Security Decision Log (v1.0.1 → v2.0)\n\n| Decision                         | Version | Rationale                                   | Impact                              |\n| -------------------------------- | ------- | ------------------------------------------- | ----------------------------------- |\n| **Adapter inside act/connector** | v1.0.1  | Zero extra hop, no centralized orchestrator | Simpler, faster, more secure        |\n| **JWT + API Keys only**          | v1.0.1  | No custom auth, standard OAuth 2.0          | Simpler, interoperable, auditable   |\n| **Pipeline sole writer**         | v1.0.1  | Single point of truth, no contention        | Simpler, no data races, clear audit |\n| **0.70/0.85 confidence**         | v1.0.1  | Balanced automation with human oversight    | Industry standard, clear governance |\n| **4 memory scopes**              | v1.0.1  | Personal, Work, Organization, AI            | Clear boundaries, RBAC-gated        |\n| **8 lens taxonomy**              | v1.0.1  | Comprehensive coverage, user-friendly       | Better UX, no user confusion        |\n| **Tenant-scoped DOs**            | v1.0.1  | No shared DOs, no cross-tenant leakage      | P0 security requirement             |\n| **Credential Wall**              | v1.0.1  | Single point of secret retrieval            | No credential sprawl, full audit    |\n| **Nango + MCP + Native**         | v1.0.1  | Three adapter types, no lock-in             | Swappable, no code changes          |\n| **5 SDK variants**               | v1.0.1  | Right-sized for every use case              | Better DX, no bundle bloat          |\n| **HITL expiry (72h)**            | v1.0.1  | Prevents stale proposals                    | Better UX, cleaner governance       |\n| **KSP anonymization**            | v1.0.1  | No tenant data crosses boundaries           | Privacy, compliance, trust          |\n| **MCP read-only default**        | v1.0.1  | Write requires explicit scope               | Security, no accidental writes      |\n| **Entity360 injection**          | v1.0.1  | Context-rich provider calls                 | Better UX, no manual context        |\n| **Fail-loud**                    | v1.0.1  | Never fail-silent                           | Security, compliance, trust         |\n\n---\n\n## 12. Deployment Architecture\n\nThis section defines the physical substrate of the IntegrateWise Continuity Bridge v2.0. It is the canonical reference for how the 26-service mesh maps to Cloudflare primitives, how data flows across those primitives, and how the platform maintains tenant isolation, observability, and deployability at scale.\n\nEvery physical primitive carries `tenant_id`. Every deployment unit is tenant-agnostic at the infrastructure level — isolation is enforced at the data layer, not by separate infrastructure per tenant.\n\n### 12.1 Service Topology\n\nAll 26 services deploy as Cloudflare Workers. All serve all tenants. Customer-Zero is a tenant class, not a separate deployment.\n\n```\n┌──────────────────────────────────────────────────────────────────────────────┐\n│                              TIER 0 — INGRESS                                 │\n│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────────────┐   │\n│  │   gateway       │    │ webhook-ingress │    │  (Pages) apps/web       │   │\n│  │   integratewise-│    │   iw-webhook-   │    │  (Pages) apps/mcp-server│   │\n│  │   gateway       │    │   ingress       │    │                         │   │\n│  │   [Worker]      │    │   [Worker]      │    │  [Cloudflare Pages]     │   │\n│  └────────┬────────┘    └────────┬────────┘    └─────────────────────────┘   │\n│           │                      │                                            │\n│           ▼                      ▼                                            │\n│  ┌────────────────────────────────────────────────────────────────────────┐  │\n│  │                     SERVICE BINDINGS (RPC)                              │  │\n│  │  gateway ──► {TENANTS, WEBHOOK_INGRESS, NORMALIZER, PIPELINE, ACT, ...} │  │\n│  └────────────────────────────────────────────────────────────────────────┘  │\n├──────────────────────────────────────────────────────────────────────────────┤\n│                              TIER 1 — TENANT & SYNC                           │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │\n│  │   tenants   │  │  connector  │  │connector-sync│  │      normalizer     │  │\n│  │  iw-tenants │  │ iw-connector│  │iw-connector- │  │    iw-normalizer    │  │\n│  │  [Worker]   │  │  [Worker]   │  │   sync      │  │     [Worker]        │  │\n│  └─────────────┘  └─────────────┘  │   [Worker]   │  └─────────────────────┘  │\n│  ┌─────────────┐                   └─────────────┘  ┌─────────────────────┐  │\n│  │    loader   │                                    │     spine-v2        │  │\n│  │  iw-loader  │                                    │     iw-spine-v2     │  │\n│  │  [Worker]   │                                    │     [Worker]        │  │\n│  └─────────────┘                                    └─────────────────────┘  │\n├──────────────────────────────────────────────────────────────────────────────┤\n│                           TIER 2 — PROJECTION & RENDERING                     │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │\n│  │   pipeline  │  │twin-orchestr│  │     l2      │  │    intelligence     │  │\n│  │ iw-pipeline │  │   ator      │  │    iw-l2    │  │   iw-intelligence   │  │\n│  │ [Worker]    │  │ [Durable    │  │  [Worker]   │  │    [Worker]         │  │\n│  │             │  │  Object]    │  │             │  │                     │  │\n│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────────────┘  │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │\n│  │  knowledge  │  │   govern    │  │    store    │  │        act          │  │\n│  │ iw-knowledge│  │  iw-govern  │  │   iw-store  │  │      iw-act         │  │\n│  │  [Worker]   │  │  [Worker]   │  │  [Worker]   │  │     [Worker]        │  │\n│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────────────┘  │\n├──────────────────────────────────────────────────────────────────────────────┤\n│                        TIER 3 — CAPABILITY RUNTIME                            │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │\n│  │   hermes    │  │   triage    │  │   workflow  │  │   iw-agent-runtime  │  │\n│  │  iw-hermes  │  │  iw-triage  │  │ iw-workflow │  │  iw-agent-runtime   │  │\n│  │ [Durable    │  │ [Worker —   │  │ [Durable    │  │     [Worker]        │  │\n│  │  Object]    │  │  in Hermes] │  │  Object]    │  │                     │  │\n│  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────────────┘  │\n│  ┌────────────────────────────────────────────────────────────────────────┐  │\n│  │                      mcp-connector (inbound MCP)                        │  │\n│  │                      iw-mcp-connector [Worker]                          │  │\n│  └────────────────────────────────────────────────────────────────────────┘  │\n├──────────────────────────────────────────────────────────────────────────────┤\n│                            TIER 4 — SUPPORTING                                │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │  │\n│  │agent-registry│  │   billing   │  │    admin    │  │folder-watcher│       │  │\n│  │iw-agent-reg │  │ iw-billing  │  │  iw-admin   │  │ iw-folder-   │       │  │\n│  │  [Worker]   │  │  [Worker]   │  │  [Worker]   │  │  watcher     │       │  │\n│  └─────────────┘  └─────────────┘  └─────────────┘  │ [Durable     │       │  │\n│  ┌─────────────┐  ┌─────────────┐                   │   Object]    │       │  │\n│  │  continuity │  │  telemetry  │                   └─────────────┘       │  │\n│  │iw-continuity│  │ iw-telemetry│  ┌─────────────┐                        │  │\n│  │  [Worker]   │  │  [Worker]   │  │    think    │                        │  │\n│  └─────────────┘  └─────────────┘  │   iw-think  │                        │  │\n│                                     │   [Worker]  │                        │  │\n│                                     └─────────────┘                        │  │\n└──────────────────────────────────────────────────────────────────────────────┘\n```\n\nThe OODA loop physically spans Tier 2 (Observe + Orient via `normalizer`, `intelligence`, `knowledge`) and Tier 3 (Decide + Act via `twin-orchestrator`, `govern`, `workflow`, `act`). The loop closes through `pipeline` writes to Spine.\n\n`spine-v2` is the normalization and graph service that supports the Adaptive Spine. `store` materializes projections (accounts, deals, tasks) for fast reads. `l2` renders the 8-lens projection overlays (department × industry).\n\n### 12.2 Cloudflare Primitives\n\nThe platform maps its 26 services to eight Cloudflare primitives. Each primitive is tenant-scoped at the data layer, never at the infrastructure layer.\n\n| Primitive           | Services Using                                                                                    | Tenant Isolation                                  | Purpose                   |\n| ------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------- |\n| **Workers**         | All 26 services                                                                                   | Stateless by design; `tenant_id` in every request | Compute mesh              |\n| **D1**              | `pipeline`, `intelligence`, `continuity`, `govern`, `tenants`, `knowledge`, `normalizer`, `store` | `WHERE tenant_id = ?` on every query              | Relational context        |\n| **Durable Objects** | `twin-orchestrator`, `hermes`, `workflow`, `folder-watcher`, `govern` (HITL)                      | `idFromName(tenant_id + suffix)`                  | Stateful compute          |\n| **KV**              | `gateway`, `connector`, `continuity`, `act`, `intelligence`                                       | `tenant_id` in every key                          | Cache & registry          |\n| **R2**              | `telemetry`, `knowledge`, `act` (exports), `normalizer` (DLQ)                                     | `tenant_id` in every object key                   | Blob & archive storage    |\n| **Queues**          | `webhook-ingress`, `connector-sync`, `normalizer`, `pipeline`, `act`, `telemetry`                 | `tenant_id` in every message envelope             | Async messaging           |\n| **Vectorize**       | `knowledge`, `continuity`, `intelligence`                                                         | `tenant_id` filter on every query                 | Semantic search           |\n| **AI Search**       | `knowledge`, `intelligence`                                                                       | `tenant_id` in metadata filter                    | Hybrid keyword + semantic |\n\n**Key Invariant:** No shared Durable Objects. No cross-tenant KV keys. No unqualified D1 queries. No queue messages without `tenant_id` in the envelope. Fail-loud on every violation.\n\n### 12.3 Service Bindings Map\n\nThe Gateway exposes all 26 services via service bindings. Internal services MAY call each other directly via bindings (bypassing Gateway for performance), but MUST still carry `tenant_id`.\n\nThe full 26×26 matrix is maintained in `apps/gateway/src/bindings.ts`. Below are the **key bindings** that define the architecture's data flow:\n\n| Caller              | Callee           | Binding          | Purpose                           |\n| ------------------- | ---------------- | ---------------- | --------------------------------- |\n| `gateway`           | `tenants`        | `TENANTS`        | JWT resolution, RBAC, plan limits |\n| `gateway`           | `normalizer`     | `NORMALIZER`     | Inbound data normalization        |\n| `gateway`           | `pipeline`       | `PIPELINE`       | Spine write (via queue)           |\n| `webhook-ingress`   | `connector-sync` | `CONNECTOR_SYNC` | Webhook → sync job                |\n| `connector-sync`    | `normalizer`     | `NORMALIZER`     | Raw records → canonical           |\n| `normalizer`        | `pipeline`       | `PIPELINE`       | Canonical entities → Spine        |\n| `pipeline`          | `intelligence`   | `INTELLIGENCE`   | Analytics, context assembly       |\n| `pipeline`          | `knowledge`      | `KNOWLEDGE`      | RAG indexing, semantic search     |\n| `pipeline`          | `continuity`     | `CONTINUITY`     | Memory consolidation, decay       |\n| `twin-orchestrator` | `govern`         | `GOVERN`         | Confidence scoring, HITL          |\n| `twin-orchestrator` | `intelligence`   | `INTELLIGENCE`   | Context assembly                  |\n| `twin-orchestrator` | `knowledge`      | `KNOWLEDGE`      | RAG retrieval                     |\n| `twin-orchestrator` | `think`          | `THINK`          | LLM reasoning                     |\n| `govern`            | `act`            | `ACT`            | Approved actions → execution      |\n| `govern`            | `workflow`       | `WORKFLOW`       | Durable orchestration             |\n| `act`               | `pipeline`       | `PIPELINE`       | Execution writeback               |\n| `hermes`            | `act`            | `ACT`            | Message queue → execution         |\n| `hermes`            | `triage`         | `TRIAGE`         | Confidence routing, decay         |\n| `workflow`          | `act`            | `ACT`            | Workflow step execution           |\n| `workflow`          | `hermes`         | `HERMES`         | Message routing                   |\n| `mcp-connector`     | `pipeline`       | `PIPELINE`       | MCP tool results → Spine          |\n| `mcp-connector`     | `intelligence`   | `INTELLIGENCE`   | MCP context assembly              |\n| `mcp-connector`     | `knowledge`      | `KNOWLEDGE`      | MCP RAG retrieval                 |\n| `loader`            | `connector-sync` | `CONNECTOR_SYNC` | Cold-start, webhook trigger       |\n| `continuity`        | `knowledge`      | `KNOWLEDGE`      | Knowledge base indexing           |\n| `continuity`        | `telemetry`      | `TELEMETRY`      | Observability events              |\n\n### 12.4 CI/CD Pipeline\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         CI/CD PIPELINE                                       │\n│                                                                              │\n│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐   │\n│  │   Push to   │───►│   Lint &    │───►│   TypeCheck │───►│    Test     │   │\n│  │   main/     │    │   Format    │    │   (tsc)     │    │   (vitest)  │   │\n│  │   feature/* │    │   (eslint)  │    │             │    │             │   │\n│  └─────────────┘    └─────────────┘    └─────────────┘    └──────┬──────┘   │\n│                                                                   │          │\n│                                                                   ▼          │\n│  ┌────────────────────────────────────────────────────────────────────────┐  │\n│  │  DEPLOY MATRIX (parallel, per service)                                  │  │\n│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐      │  │\n│  │  │ gateway │  │webhook- │  │ tenants │  │ connector│  │  ...    │      │  │\n│  │  │         │  │ ingress │  │         │  │  sync   │  │         │      │  │\n│  │  │wrangler │  │wrangler │  │wrangler │  │wrangler │  │wrangler │      │  │\n│  │  │ deploy  │  │ deploy  │  │ deploy  │  │ deploy  │  │ deploy  │      │  │\n│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘  └─────────┘      │  │\n│  │                                                                         │  │\n│  │  Rollback: If any deploy fails, auto-rollback to previous version       │  │\n│  │  Canary: 10% traffic → 50% → 100% over 15 minutes (gateway only)        │  │\n│  └────────────────────────────────────────────────────────────────────────┘  │\n│                                                                   │          │\n│                                                                   ▼          │\n│  ┌────────────────────────────────────────────────────────────────────────┐  │\n│  │  POST-DEPLOY VERIFICATION                                               │  │\n│  │  • Health check: GET /health on every service                           │  │\n│  │  • Discovery contract test: Verify /api/v1/discovery schema             │  │\n│  │  • MCP smoke test: Connect, list tools, invoke ping                     │  │\n│  │  • E2E: Connect Salesforce → sync → propose → approve (Customer-Zero)   │  │\n│  └────────────────────────────────────────────────────────────────────────┘  │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\nThe GitHub Actions workflow has three stages:\n\n1. **Quality Gates:** `lint` (eslint), `typecheck` (tsc), `test` (vitest). All three run in parallel on every PR and push.\n2. **Deploy Matrix:** One job per service, running `wrangler deploy` with the environment inferred from branch (`main` → production, else staging). `fail-fast: false` ensures one failed deploy does not cancel the others. Gateway deploys include a 15-minute canary (10% → 50% → 100%).\n3. **Post-Deploy E2E:** A smoke test suite connects to a test tenant, runs a full sync → propose → approve happy path, and verifies discovery contract and MCP connectivity.\n\n**Rollback:** Workers roll back via `wrangler deploy --rollback` or by redeploying the previous git SHA. D1 schema rollbacks use automated D1 backups plus manual migration reversal. KV and R2 are not versioned; they rely on TTL and lifecycle rules.\n\n### 12.5 Local Development Stack\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         LOCAL DEV STACK                                      │\n│                                                                              │\n│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐   │\n│  │   pnpm      │    │  Wrangler   │    │  Miniflare  │    │  Local D1   │   │\n│  │  workspace  │◄──►│   CLI       │◄──►│  (local     │◄──►│  (sqlite)   │   │\n│  │             │    │  (dev)      │    │  Workers)   │    │             │   │\n│  └─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘   │\n│         │                                                                  │\n│         ▼                                                                  │\n│  ┌────────────────────────────────────────────────────────────────────────┐│\n│  │  LOCAL SERVICES (all 26 stubs)                                         ││\n│  │  $ pnpm dev  → starts Wrangler dev server for each service             ││\n│  │  $ pnpm dev:gateway  → gateway only                                    ││\n│  │  $ pnpm dev:all  → parallel dev (requires 16GB+ RAM)                   ││\n│  └────────────────────────────────────────────────────────────────────────┘│\n│         │                                                                  │\n│         ▼                                                                  │\n│  ┌────────────────────────────────────────────────────────────────────────┐│\n│  │  LOCAL MCP BRIDGE                                                      ││\n│  │  scripts/integratewise-mcp-bridge.mjs                                  ││\n│  │  stdio → SSE bridge for local Cursor/Claude testing                    ││\n│  └────────────────────────────────────────────────────────────────────────┘│\n│         │                                                                  │\n│         ▼                                                                  │\n│  ┌────────────────────────────────────────────────────────────────────────┐│\n│  │  MOCK PROVIDERS                                                        ││\n│  │  • Nango dev mode (localhost:3009)                                     ││\n│  │  • Local Salesforce mock (msw handlers)                                ││\n│  │  • Local Stripe webhook simulator                                      ││\n│  └────────────────────────────────────────────────────────────────────────┘│\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\nThe local stack uses a pnpm monorepo with Wrangler CLI for local Workers simulation. Each service gets its own port (`gateway` on 8787, `mcp-server` on 8788, `act` on 8790, etc.). D1 runs as a local SQLite file. KV and Queues run in-memory. The MCP bridge (`scripts/integratewise-mcp-bridge.mjs`) converts stdio to SSE so local AI assistants (Cursor, Claude Desktop) can test against the local stack without cloud deployment.\n\nKey commands:\n\n- `pnpm dev` — starts all services in parallel\n- `pnpm dev:gateway` — gateway only, for frontend-only work\n- `pnpm db:migrate` — applies D1 migrations locally\n- `pnpm mcp:local` — starts the stdio→SSE bridge\n- `pnpm test:e2e` — Playwright end-to-end against local stack\n\n### 12.6 Per-App Deployment Subsets\n\nNot all 26 services deploy together. The dependency graph and blast radius determine safe subsets.\n\n| Subset         | Services                                           | Trigger              | Risk                           | Schema Change? |\n| -------------- | -------------------------------------------------- | -------------------- | ------------------------------ | -------------- |\n| **Hotfix**     | Single service                                     | Critical bug         | Low — no schema changes        | No             |\n| **Frontend**   | `apps/web` + `apps/mcp-server`                     | UI release           | Low — no backend deps          | No             |\n| **Spine**      | `pipeline` + `normalizer` + `spine-v2`             | Schema change        | Medium — D1 migration required | Yes            |\n| **Capability** | `act` + `hermes` + `workflow`                      | New provider adapter | Medium — test outbound calls   | No             |\n| **AI**         | `think` + `twin-orchestrator` + `iw-agent-runtime` | Model change         | Low — swappable adapter        | No             |\n| **Full**       | All 26                                             | Major version        | High — full e2e required       | Maybe          |\n| **Infra**      | D1 migrations + KV config + Vectorize              | Index change         | High — backup first            | Yes            |\n\n**Verification per subset:**\n\n- `hotfix`: health check + binding RPC ping + queue depth check\n- `spine`: health + bindings + queue depth + D1 schema version + discovery contract + e2e happy path\n- `full`: all checks + MCP smoke test + full Customer-Zero e2e\n\n### 12.7 The 7 Missing Services (Stub Specs)\n\nThese seven services are listed in the 26-service topology but have minimal v2.0 specifications. They are reserved for future expansion and are currently implemented as thin stubs with health endpoints and service bindings.\n\n| Service             | Tier   | Type               | Current Stub                                                                | Future Role                                                                                 |\n| ------------------- | ------ | ------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| `twin-orchestrator` | Tier 2 | Durable Object     | DO class with `reason` + `propose` actions; snapshots to D1 every 5 minutes | Persistent per-user OODA engine; multi-step reasoning chains; proactive proposal generation |\n| `hermes`            | Tier 3 | Durable Object     | Message queue head/tail; 30s alarm batch processing                         | Full message broker with dead-letter handling, retry policies, and priority routing         |\n| `triage`            | Tier 3 | Worker (in Hermes) | Confidence scoring stub; routes to Active/Staging/Discard                   | Memory triage automation; decay scheduling; reinforcement tracking                          |\n| `iw-agent-runtime`  | Tier 3 | Worker             | MCP-compliant agent execution stub; health endpoint                         | Full agent runtime with tool use, multi-turn reasoning, and sandboxed execution             |\n| `agent-registry`    | Tier 4 | Worker             | Catalog of agent names and capabilities; no runtime                         | Dynamic agent discovery, A2A protocol registry, agent capability negotiation                |\n| `folder-watcher`    | Tier 4 | Durable Object     | File tree snapshot + checksum; 60s alarm                                    | Real-time filesystem sync; macOS chokidar → DO event stream; bulk import trigger            |\n| `telemetry`         | Tier 4 | Worker             | Fire-and-forget queue consumer; writes to R2                                | Full observability: metrics, traces, logs, alerting, dashboards                             |\n\nEach stub exposes `GET /health`, participates in service bindings, and queues messages with `tenant_id`. They are safe to deploy as no-ops and will be fleshed out in subsequent releases without breaking the topology.\n\n---\n\n## 13. Continuity Pipelines & Workers\n\nThe Continuity Layer is not a single service. It is a **system of pipelines** — eight asynchronous workers that move data from raw provider signals to canonical Spine entities, from transient memory to compounding knowledge, from reasoning proposals to governed actions. Each pipeline has a personality, a trigger, a stage sequence, and a defined output.\n\nThese workers run continuously. They do not sleep. They are the heartbeat of the platform.\n\n### What Workers Do Every Day\n\n**Morning Pre-Load (06:00 UTC):** `connector-sync` wakes up for every tenant with a daily sync schedule. It checks Nango for tokens that need refresh, queues creamy sync jobs for stale connectors, and warms the KV discovery cache so that when users open their dashboards, the data is already there. `continuity` runs the decay evaluator, checking which memories have crossed the 30-day threshold and should be demoted from Active to Staging.\n\n**During Work (Real-Time):** `webhook-ingress` catches provider webhooks in milliseconds. `loader` verifies signatures and triggers incremental syncs. `normalizer` parses, detects traits, resolves entities, and enqueues canonical batches. `pipeline` writes to Spine — the only writer — and emits events that `intelligence` and `knowledge` consume for context assembly and RAG indexing. `twin-orchestrator` (the DO) reasons over Spine changes, proposes actions, and sends them to `govern` for confidence scoring.\n\n**Evening Consolidation (18:00 UTC):** `continuity` compiles the day's memory into the Knowledge corpus. `knowledge` re-indexes documents and embeddings. `triage` reviews the day's staging memories and promotes high-confidence ones to Active. `govern` expires proposals in My Desk that have sat for 72 hours without review.\n\n**Nightly Triage (02:00 UTC):** `telemetry` processes the day's observability blobs. `hermes` retries dead-letter messages. `folder-watcher` checksums watched folders and emits change events. `twin-orchestrator` snapshots all active DO states to D1 for disaster recovery.\n\n---\n\n### 13.1 Ingestion Pipeline\n\n**What it does:** Catches everything from the outside world — webhooks, sync polls, file drops, MCP tool results — and turns it into a verified, deduplicated, tenant-scoped event.\n\n**Trigger:** Provider webhook, cron sync schedule, manual sync button, Nango OAuth callback, MCP tool invocation, file system change.\n\n**Stages:**\n\n1. **Receive:** `webhook-ingress` accepts HTTP POST, validates signature, extracts provider ID.\n2. **Deduplicate:** `loader` checks KV idempotency key; rejects duplicates.\n3. **Classify:** `loader` classifies event type (`auth.created`, `webhook.received`, `nango.sync`).\n4. **Route:** Enqueues to `CONNECTOR_SYNC_QUEUE` with `tenant_id`, `provider`, `syncType`, `since`.\n\n**Output:** `SyncJob` messages on `CONNECTOR_SYNC_QUEUE`.\n\n```\n[Provider Webhook] ──► [webhook-ingress] ──► validate signature ──► [loader]\n                                                                    │\n                                                                    ▼\n                                                            [deduplicate]\n                                                                    │\n                                                                    ▼\n                                                            [classify event]\n                                                                    │\n                                                                    ▼\n                                                    [CONNECTOR_SYNC_QUEUE]\n```\n\n### 13.2 Normalization Pipeline\n\n**What it does:** Transforms raw provider records into canonical Spine entities. This is the only path from inbound data to the Spine. Every record that enters the Spine has been through all 8 stages.\n\n**Trigger:** `NORMALIZER_QUEUE` consumer batch (max 50, timeout 10s).\n\n**Stages:**\n\n1. **NA0 — Parse:** JSON/CSV/XML → structured object.\n2. **NA1 — Trait Detect:** Identify entity type from field heuristics.\n3. **NA2 — Resolve Type:** Map to canonical entity type from Schema Provider.\n4. **NA3 — Resolve Entity:** SSOC lookup or mint new stable UUID.\n5. **NA4 — Link Relations:** Traverse foreign keys, infer relationships.\n6. **NA5 — Enrich & Map:** Apply tenant-specific field mappings, enrich from Knowledge.\n7. **NA6 — Validate:** Schema validation against Schema Provider contract.\n8. **NA7 — Emit Canonical:** Serialize to `CanonicalEntityBatch`, enqueue to `PIPELINE_QUEUE`.\n\n**Output:** `CanonicalEntityBatch` messages on `PIPELINE_QUEUE`.\n\n```\n[Raw Record] ──► NA0 ──► NA1 ──► NA2 ──► NA3 ──► NA4 ──► NA5 ──► NA6 ──► NA7\n                 Parse   Trait   Resolve Resolve  Link   Enrich  Validate Emit\n                         Detect  Type    Entity   Rels   & Map   Canonical\n                                                                     │\n                                                                     ▼\n                                                          [PIPELINE_QUEUE]\n```\n\n### 13.3 Resolution Pipeline\n\n**What it does:** Resolves conflicts between provider records and existing Spine entities. When Salesforce says a contact's email is `alice@acme.com` and HubSpot says `alice.smith@acme.com`, the Resolution Pipeline decides which one wins — or creates a merge proposal.\n\n**Trigger:** `pipeline` detects a conflicting `sourceId` or `canonicalName` during write.\n\n**Stages:**\n\n1. **Detect:** `pipeline` flags a conflict in the `spine_audit_log`.\n2. **Fetch:** `intelligence` queries both provider records and the existing SSOC entity.\n3. **Score:** `intelligence` computes a confidence score for each source based on freshness, provider authority, and historical accuracy.\n4. **Decide:** If confidence delta ≥ 0.20, auto-resolve to the higher-confidence source. If delta < 0.20, create a `merge_proposal` for HITL review.\n5. **Write:** `pipeline` applies the resolution (or queues the proposal).\n\n**Output:** Resolved entity update or `merge_proposal` in `action_proposals` table.\n\n```\n[Pipeline Conflict] ──► [intelligence] ──► fetch both sources ──► score confidence\n                                                                  │\n                                                                  ▼\n                                                           [decide winner]\n                                                                  │\n                                                    ┌─────────────┼─────────────┐\n                                                    │             │             │\n                                                    ▼             ▼             ▼\n                                              [auto-resolve]  [merge_proposal]  [tie]\n                                                    │             │             │\n                                                    ▼             ▼             ▼\n                                              [pipeline write] [HITL review] [discard]\n```\n\n### 13.4 Timeline Pipeline\n\n**What it does:** Builds the immutable timeline of every entity — when it was first seen, when it changed, who changed it, what the provider said, what the Twin proposed. This is the audit trail that powers Entity360.\n\n**Trigger:** Every `pipeline` write to `entities` or `relationships`.\n\n**Stages:**\n\n1. **Capture:** `pipeline` writes the `spine_audit_log` entry with `previousRecord` and `record`.\n2. **Enrich:** `intelligence` adds sentiment, health score, and relationship deltas.\n3. **Index:** `knowledge` indexes significant timeline events (merges, status changes, governance decisions) into Vectorize for semantic retrieval.\n4. **Emit:** `continuity` emits a `timeline_event` to the `TELEMETRY_QUEUE` for observability.\n\n**Output:** `spine_audit_log` rows, Vectorize timeline index, telemetry events.\n\n```\n[Pipeline Write] ──► [spine_audit_log] ──► [intelligence enrich]\n                                                    │\n                                                    ▼\n                                           [knowledge index]\n                                                    │\n                                                    ▼\n                                           [continuity emit]\n                                                    │\n                                                    ▼\n                                           [TELEMETRY_QUEUE]\n```\n\n### 13.5 Knowledge Pipeline\n\n**What it does:** Transforms documents, SOPs, playbooks, andTwin-generated insights into searchable, semantic knowledge. This is the RAG layer that feeds Context Assembly.\n\n**Trigger:** Document upload, sync-generated document, Twin insight with `twin_generated` source, nightly cron re-index.\n\n**Stages:**\n\n1. **Ingest:** Document stored in R2 (`R2_ASSETS`).\n2. **Chunk:** `think` service applies semantic chunking (sentence boundaries for prose, header boundaries for SOPs, sliding window for code/markdown).\n3. **Embed:** `think` generates 768-dimensional embeddings via OpenRouter (`gpt-5-mini` default).\n4. **Index:** `knowledge` writes vectors to `VECTORIZE_KNOWLEDGE` with `tenant_id`, `scope`, `type`, `source` metadata.\n5. **Retrieve:** During Context Assembly, `intelligence` queries Vectorize with hybrid keyword + semantic search.\n\n**Output:** Vectorize index entries, R2 blob references, retrievable `KnowledgeChunk` arrays.\n\n```\n[Document Upload] ──► [R2_ASSETS] ──► [think chunk]\n                                            │\n                                            ▼\n                                     [think embed]\n                                            │\n                                            ▼\n                                     [VECTORIZE_KNOWLEDGE]\n                                            │\n                                            ▼\n                                     [intelligence retrieve]\n```\n\n### 13.6 R2 Triage Pipeline\n\n**What it does:** Manages the lifecycle of large objects in R2 — sync blobs, telemetry archives, DLQ dumps, and generated exports. It enforces retention policies, compresses cold data, and deletes expired objects.\n\n**Trigger:** Nightly cron (02:00 UTC), or quota threshold breach.\n\n**Stages:**\n\n1. **Scan:** `folder-watcher` (or `continuity`) lists R2 prefixes by tenant and date.\n2. **Classify:** Objects are classified by retention policy (`standard` 90 days, `extended` 1 year, `permanent` 7 years).\n3. **Compress:** Objects older than 30 days are gzip-compressed in-place.\n4. **Archive:** Objects past retention are moved to cold storage or deleted (with audit log).\n5. **Report:** `telemetry` emits a `storage_audit` event with bytes reclaimed per tenant.\n\n**Output:** Compressed R2 objects, deletion audit logs, storage quota reports.\n\n```\n[Nightly Cron] ──► [R2 scan] ──► [classify retention] ──► [compress old]\n                                                              │\n                                                              ▼\n                                                       [archive / delete]\n                                                              │\n                                                              ▼\n                                                       [TELEMETRY_QUEUE]\n```\n\n### 13.7 Projection Pipeline\n\n**What it does:** Materializes the 8-lens projections (accounts, contacts, deals, tasks, tickets, content, knowledge, events) from Spine entities into fast-read views in `store` and `l2`. This is what the Workbench and MCP tool catalog surface to users.\n\n**Trigger:** `pipeline` write to `entities` or `relationships`, or manual refresh.\n\n**Stages:**\n\n1. **Detect:** `pipeline` emits a `projection_update` event after Spine write.\n2. **Map:** `l2` maps the canonical entity to the tenant's 8-lens projection schema (department × industry overlay).\n3. **Render:** `store` materializes the projection view (e.g., \"Accounts with Open Deals > $10k\").\n4. **Cache:** `store` writes the projection to KV with a 300-second TTL.\n5. **Invalidate:** On next entity change, `pipeline` emits a cache invalidation.\n\n**Output:** Materialized projection views in `store`, cached in KV, served via Discovery API.\n\n```\n[Pipeline Write] ──► [projection_update event] ──► [l2 map]\n                                                         │\n                                                         ▼\n                                                  [store render]\n                                                         │\n                                                         ▼\n                                                  [KV cache]\n                                                         │\n                                                         ▼\n                                                  [Discovery API]\n```\n\n### 13.8 Continuity Orchestrator Pipeline\n\n**What it does:** The master pipeline that coordinates memory decay, cross-scope promotion, nightly consolidation, and disaster recovery snapshots. It is the janitor, the librarian, and the backup operator of the platform.\n\n**Trigger:** Cron schedule (hourly for decay, nightly for consolidation, weekly for snapshots).\n\n**Stages:**\n\n1. **Decay:** `continuity` scans Active memory for records with no access or reinforcement in 30 days. Demotes to Staging. Staging records with no access in 90 days are Archived to R2.\n2. **Promote:** `triage` scans Staging memories with recent reinforcement or high query frequency. Promotes to Active with a new confidence score.\n3. **Consolidate:** `continuity` merges duplicate memories, compiles personal insights into Organization knowledge (with governance approval if cross-scope), and updates the Continuity Graph.\n4. **Snapshot:** `continuity` triggers D1 backup to R2 and DO state snapshots for all active tenants.\n\n**Output:** Tier-migrated memory records, consolidated knowledge documents, R2 backups, DO snapshots.\n\n```\n[Hourly Cron] ──► [decay scan] ──► [demote to staging]\n                                        │\n                                        ▼\n                                 [triage promote]\n                                        │\n                                        ▼\n                                 [nightly consolidate]\n                                        │\n                                        ▼\n                                 [weekly snapshot]\n                                        │\n                                        ▼\n                                 [R2_BACKUP] + [DO_SNAPSHOT]\n```\n\n---\n\n## 14. The One Invariant\n\nThe following invariant is the single immutable law of the IntegrateWise Continuity Bridge v2.0. It has been updated from v1.0.1 §12 to reflect the product layer, the Twin's ambient nature, and the ecosystem architecture.\n\n> Identity gates the door. Discovery advertises the capabilities. The `act` and `connector` services are the only hands that touch outside tools (through their adapter modules). Spine is the only place context is written. Govern sits between reasoning and execution. Memory is scoped and compounds. Agents and tools coordinate through the Spine, never directly. Every hop carries `tenant_id`. The Twin is ambient, not separate. The ecosystem is a capability fabric. The moat is continuity.\n\nThis invariant is not aspirational. It is enforced by code:\n\n- `pipeline` is the only service with `INSERT`/`UPDATE` privileges on `entities` and `relationships` (`db-gate` rejects all others).\n- `act` and `connector` are the only services that hold provider credentials (`Credential Wall`).\n- `govern` is the only service that can transition an `ActionProposal` to `approved`.\n- Every service binding, queue message, D1 query, KV key, R2 object key, and DO ID includes `tenant_id`.\n- No adapter module is bypassed. No direct provider calls from `intelligence`, `knowledge`, or `twin-orchestrator`.\n- No peer-to-peer agent communication. All agent coordination routes through the Spine.\n- The Twin is not a service you call. It is a reasoning quality that runs through `intelligence`, `continuity`, and `knowledge`.\n\nBreak this invariant and you break the architecture.\n\n---\n\n## 15. Glossary\n\n| Term                    | Definition                                                                                                                                                                    |\n| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Adaptive Spine**      | The directed property graph of all canonical entities and relationships. The single source of context. Written only by `pipeline`.                                            |\n| **ADK**                 | Agent Development Kit — the framework for building and deploying AI agents that connect to the IntegrateWise ecosystem via MCP.                                               |\n| **A2A**                 | Agent-to-Agent — the protocol by which agents discover and communicate with each other through the Spine, never directly.                                                     |\n| **Capability**          | A named, discoverable action that the platform can perform (e.g., `salesforce.opportunity.update`). Defined in the Capability Registry, gated by Governance.                  |\n| **Capability Fabric**   | The ecosystem of tools, agents, and services that expose capabilities through a unified Discovery and Spine interface.                                                        |\n| **Connector**           | The service that handles inbound provider connections — OAuth callbacks, webhook verification, and adapter selection.                                                         |\n| **Context Assembly**    | The process of building Entity360 by combining Spine entities, Memory, Knowledge, and real-time provider state into a single context window.                                  |\n| **Continuity Bridge**   | The public interface layer of the platform — identity, auth, discovery, projections, capabilities, governance entry, and events.                                              |\n| **Continuity Engine**   | The internal substrate that preserves, compounds, and governs organizational context across time, tools, and agents.                                                          |\n| **Credential Wall**     | The security boundary where per-tenant provider credentials are fetched from Cloudflare Secrets or Nango Vault and never exposed to services or logs.                         |\n| **Discovery**           | The API endpoint (`/api/v1/discovery`) that advertises all capabilities, projections, and endpoints available to a tenant, filtered by RBAC.                                  |\n| **Durable Object (DO)** | Cloudflare's stateful compute primitive with transactional storage and alarms. Used for `twin-orchestrator`, `hermes`, `workflow`, `folder-watcher`, and HITL.                |\n| **Entity360**           | The assembled context for a single entity: its properties, relations, memory, timeline, sentiment, and health score.                                                          |\n| **Entity Graph**        | The directed property graph of `entities` and `relationships` in the Adaptive Spine.                                                                                          |\n| **Governance**          | The approval and confidence-scoring layer between reasoning and execution. Enforces the 0.70/0.85 law.                                                                        |\n| **HITL**                | Human-in-the-Loop — the orchestration of human approval within automated workflows, implemented as a Durable Object per `tenant_id + user_id`.                                |\n| **Integration Manager** | _Removed in v1.0.1._ The adapter pattern now lives inside `act` and `connector` services. Not a separate service.                                                             |\n| **KSP**                 | Knowledge Sharing Protocol — the anonymized, cross-tenant mechanism for sharing capability patterns without exposing tenant data.                                             |\n| **Lens**                | One of the 8 projection categories (Accounts, Contacts, Deals, Tasks, Tickets, Content, Knowledge, Events) that the Workbench surfaces to users.                              |\n| **MCP**                 | Model Context Protocol — the standard by which AI assistants connect to the IntegrateWise platform (inbound) and by which the platform connects to provider tools (outbound). |\n| **Memory**              | Compounding context scoped to Personal, Work, Organization, or AI tiers. Decays without reinforcement.                                                                        |\n| **Memory Triage**       | The confidence-scoring and routing logic that promotes, stages, or discards memories based on the 0.70/0.85 thresholds.                                                       |\n| **Moat**                | The competitive advantage derived from continuity — the compounding context that makes switching to another platform costly because the context would be lost.                |\n| **Normalizer**          | The 8-stage pipeline (NA0–NA7) that transforms raw provider records into canonical Spine entities.                                                                            |\n| **OODA**                | Observe → Orient → Decide → Act. The four-phase decision cycle that runs across the Capability, Continuity, and Governance layers.                                            |\n| **Overlay**             | A tenant-specific or department-specific customization of the base schema, projection rendering, or governance rules without modifying the core platform.                     |\n| **Pipeline**            | The asynchronous worker that is the **sole writer** to the Adaptive Spine. Enforces `tenant_id` on every write.                                                               |\n| **Projection**          | A materialized view of Spine entities optimized for a specific lens or use case (e.g., \"Open Deals by Stage\").                                                                |\n| **Provider Fabric**     | The swappable layer of external SaaS providers, APIs, and runtimes — accessed only through adapter modules inside `act` and `connector`.                                      |\n| **RBAC**                | Role-Based Access Control — tenant-scoped roles (owner, tam, account_success) with permission grants on resources.                                                            |\n| **Resolution**          | The process of resolving conflicting provider records into a single canonical entity or a merge proposal.                                                                     |\n| **Service Binding**     | Cloudflare Workers RPC mechanism — one Worker calls another directly over the internal network, carrying `tenant_id` implicitly.                                              |\n| **Spine**               | Short for Adaptive Spine. The single source of truth for entity context.                                                                                                      |\n| **SSOC**                | Stable Source-Object Correlation — a stable UUID assigned to every entity that survives provider swaps.                                                                       |\n| **Twin**                | The ambient AI reasoning engine that permeates Context Assembly, Memory, and Knowledge. Not a separate service; implemented as a Durable Object.                              |\n| **Triage**              | The service that applies confidence scoring to both execution proposals (Governance Triage) and memory records (Memory Triage).                                               |\n| **Workbench**           | The unified web interface (Next.js) where users interact with projections, capabilities, My Desk, and the Agent Registry.                                                     |\n| **Workflow**            | The durable orchestration service (Durable Object) that manages long-running, multi-step processes with pause, resume, retry, and saga compensation.                          |\n\n---\n\n## 16. Changelog\n\n| Version    | Date       | Changes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Status      |\n| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |\n| **v2.0.0** | 2026-07-03 | Complete rewrite. Unified product narrative with technical architecture. Added 6-layer architecture (Identity → Ingress → Capability → Continuity → Governance → Provider Fabric). Added OODA runtime. Added product layer (Workbench, Workbench Agent, Agent Registry, Ecosystem). Added ecosystem architecture (KSP, A2A). Added memory triage and decay pipelines. Added 8 continuity pipelines with worker narratives. Added deployment architecture with Cloudflare primitives, service topology, CI/CD, local dev stack, and per-app deployment subsets. Added security decision log documenting all v1.0.1 → v2.0 transitions. Added UI specification (Workbench, My Desk, Discovery, Capability Catalog). Added 30-term glossary. Supersedes v1.0.1-FINAL. | **CURRENT** |\n| **v1.0.1** | 2026-07-02 | Removed Integration Manager as separate service. Adapter pattern folded into `act` and `connector` services. No extra hop, no extra service. Locked.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Superseded  |\n| **v1.0.0** | 2026-07-02 | Initial locked architecture. 26 services, 4 conceptual layers, MCP pools (inbound + outbound), 0.70/0.85 governance law, Credential Wall, 8-lens taxonomy, 5 SDK variants.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Superseded  |\n\n---\n\n**END OF DOCUMENT**\n\n_IntegrateWise Continuity Bridge — Canonical Architecture Specification v2.0.0. The One Invariant is law. The adapter pattern is the Integration Manager. The Twin is ambient. The moat is continuity._\n"}