{"slug":"architecture/v2-continuity-governance","title":"§5 — Continuity Layer & Governance (The Moat)","section":"architecture","sectionLabel":"Architecture","status":"unclassified","type":"document","path":"architecture/v2-continuity-governance.mdx","bytes":85953,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n> **Status: SUPERSEDED DEEP-DIVE.** Confidence-based auto-approval, old\n> layer/product language and conflicting storage rules are historical. Current\n> Governance requires the frozen approval path in\n> [`ARCHITECTURE_LOCK.md`](./ARCHITECTURE_LOCK.md).\n\n**Status:** DRAFT — Stage 1/2 Deep-Dive for v2.0 Canonical Architecture  \n**Domain Owner:** Continuity & Governance Architecture Team  \n**Scope:** Adaptive Spine, Connectors, Loader, Synchronization, Normalizer, Schema Provider, Workspace, Memory, Knowledge, Governance Rules, Approval Gates, HITL Orchestrator, 0.70/0.85 Confidence Law, Audit, Compliance  \n**Layer Position:** Layer 4 (Continuity) + Layer 5 (Governance) in the 6-layer stack  \n**Adjacent Layers:** Capability (Layer 3, north-bound) → Continuity (Layer 4) → Governance (Layer 5) → Provider Fabric (Layer 6, south-bound)  \n**OODA Span:** Observe + Orient phases run across Capability and Continuity. Decide + Act phases are gated by Governance.  \n**Twin Posture:** AMBIENT — the Twin is not a separate entity; it is a reasoning quality that permeates Context Assembly, Memory, and Knowledge.\n\n---\n\n## 5.1 Domain Overview\n\nThe 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. 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**The 6-Layer Architecture Context:**\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│  Layer 1 — Identity          │  Who. JWT, tenant_id, RBAC, SSO.              │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  Layer 2 — Ingress           │  How. Gateway, webhooks, MCP inbound, SSE.    │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  Layer 3 — Capability        │  What. Named actions, tools, projections.     │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  Layer 4 — CONTINUITY  ◄─────┤  Context. Spine, Memory, Knowledge, Workspace.│\n│  (THIS DOMAIN)               │  The moat. Nothing passes without leaving a   │\n│                              │  trace in the Spine, the Memory Store, or the │\n│                              │  Audit Log.                                   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  Layer 5 — GOVERNANCE  ◄─────┤  Approval. Confidence gates, HITL, rules.     │\n│  (THIS DOMAIN)               │  The airlock. Every action ≥0.70 is scored.   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│  Layer 6 — Provider Fabric   │  Execution. Adapters, Nango, MCP outbound.    │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n**OODA Across Layers:**\n\n```\n      Layer 3 (Capability)          Layer 4 (Continuity)          Layer 5 (Governance)\n      ┌───────────────┐            ┌───────────────┐            ┌───────────────┐\n      │   OBSERVE     │───────────►│   ORIENT      │───────────►│    DECIDE     │\n      │  (ingress,    │  raw sig   │  (normalize,  │  entity360 │  (twin reason,│\n      │   webhooks)   │            │   resolve,    │            │   confidence) │\n      └───────────────┘            │   enrich)     │            └───────┬───────┘\n                                     └───────────────┘                    │\n                                                                          ▼\n                                                               ┌───────────────┐\n                                                               │     ACT       │\n                                                               │  (governed    │\n                                                               │   execution)  │\n                                                               └───────────────┘\n```\n\n---\n\n## 5.2 Adaptive Spine\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### 5.2.1 Spine Schema (D1)\n\n```typescript\n// packages/spine-schema/src/schema/spine.ts\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', ...\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\" }), // Normalizer-detected 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### 5.2.2 SSOC (Stable Source-Object Correlation)\n\nEvery entity in the Spine 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### 5.2.3 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---\n\n## 5.3 Connectors, Loader & Synchronization\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### 5.3.1 Connector Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                        INBOUND DATA PATH (North-to-Spine)                   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   Provider Webhooks          │   Sync Polls         │   File Drops         │\n│   ├─ Salesforce              │   ├─ 15-min cron     │   ├─ Folder Watcher  │\n│   ├─ HubSpot                 │   ├─ Nango sync      │   ├─ Bulk CSV upload │\n│   ├─ Stripe                  │   ├─ MCP tool result │   └─ API bulk import │\n│   └─ Slack                   │   └─ Custom adapter  │                      │\n│          │                   │         │            │         │            │\n│          ▼                   │         ▼            │         ▼            │\n│   ┌─────────────┐            │   ┌─────────────┐    │   ┌─────────────┐    │\n│   │ webhook-    │            │   │ connector-  │    │   │  folder-    │    │\n│   │ ingress     │            │   │ sync        │    │   │  watcher    │    │\n│   │ (Worker)    │            │   │ (Worker)    │    │   │  (DO)       │    │\n│   └──────┬──────┘            │   └──────┬──────┘    │   └──────┬──────┘    │\n│          │                   │          │            │          │           │\n│          └───────────────────┴──────────┴────────────┴──────────┘           │\n│                                    │                                       │\n│                                    ▼                                       │\n│                            ┌─────────────┐                                 │\n│                            │   LOADER    │                                 │\n│                            │  (Worker)   │                                 │\n│                            │             │                                 │\n│                            │ • Nango auth│                                 │\n│                            │   webhook   │                                 │\n│                            │   handler   │                                 │\n│                            │ • Signature │                                 │\n│                            │   verify    │                                 │\n│                            │ • Creamy    │                                 │\n│                            │   trigger   │                                 │\n│                            └──────┬──────┘                                 │\n│                                   │                                        │\n│                                   ▼                                        │\n│                         CONNECTOR_SYNC_QUEUE                               │\n│                                   │                                        │\n│                                   ▼                                        │\n│                            ┌─────────────┐                                 │\n│                            │  connector  │                                 │\n│                            │  -sync      │                                 │\n│                            │  (Worker)   │                                 │\n│                            │             │                                 │\n│                            │ • Adapter   │                                 │\n│                            │   selection │                                 │\n│                            │ • Credential│                                 │\n│                            │   fetch     │                                 │\n│                            │ • Raw fetch │                                 │\n│                            └──────┬──────┘                                 │\n│                                   │                                        │\n│                                   ▼                                        │\n│                         NORMALIZER_QUEUE                                   │\n│                                   │                                        │\n│                                   ▼                                        │\n│                            ┌─────────────┐                                 │\n│                            │  normalizer │                                 │\n│                            │  (Worker)   │                                 │\n│                            └──────┬──────┘                                 │\n│                                   │                                        │\n│                                   ▼                                        │\n│                         PIPELINE_QUEUE                                     │\n│                                   │                                        │\n│                                   ▼                                        │\n│                            ┌─────────────┐                                 │\n│                            │   pipeline  │                                 │\n│                            │ (Sole Writer│                                 │\n│                            │  to Spine)  │                                 │\n│                            └──────┬──────┘                                 │\n│                                   │                                        │\n│                                   ▼                                        │\n│                            ┌─────────────┐                                 │\n│                            │   SPINE     │                                 │\n│                            │  (D1)       │                                 │\n│                            └─────────────┘                                 │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.3.2 Connector Sync Service\n\n```typescript\n// services/connector-sync/src/sync.ts\n\nexport interface SyncJob {\n  id: string;\n  tenantId: string;\n  connectorId: string; // 'salesforce', 'hubspot', 'slack', ...\n  provider: string;\n  syncType: \"full\" | \"incremental\" | \"webhook\" | \"creamy\";\n  since: Date; // Last successful sync timestamp\n  triggeredBy: \"cron\" | \"webhook\" | \"manual\" | \"loader\";\n  status: \"queued\" | \"running\" | \"completed\" | \"failed\" | \"dlq\";\n  recordsFetched: number;\n  recordsNormalized: number;\n  recordsRejected: number;\n  errorMessage?: string;\n  startedAt?: Date;\n  completedAt?: Date;\n}\n\nexport interface SyncAdapter {\n  // Fetches raw records from the provider\n  fetch(credentials: Credentials, config: ConnectorConfig, since: Date): Promise<RawRecord[]>;\n\n  // Returns the provider's native schema for a resource\n  getSchema(resource: string): Promise<ProviderSchema>;\n\n  // Validates that the connector config is healthy\n  healthCheck(credentials: Credentials): Promise<HealthStatus>;\n}\n\nexport async function runSync(job: SyncJob): Promise<SyncResult> {\n  // 1. Tenant isolation: verify connector belongs to tenant\n  const config = await getConnectorConfig(job.connectorId, job.tenantId);\n  if (!config || config.tenantId !== job.tenantId) {\n    throw new SyncError(\"CROSS_TENANT_CONNECTOR\", 403);\n  }\n\n  // 2. Credential wall: fetch from Nango vault or CF Secrets\n  const credentials = await getCredentials(config.provider, job.tenantId);\n\n  // 3. Select sync adapter (swappable — see Adapter Pattern)\n  const adapter = selectSyncAdapter(config.provider);\n\n  // 4. Fetch raw records\n  const rawRecords = await adapter.fetch(credentials, config, job.since);\n\n  // 5. Rate-limit safety valve\n  if (rawRecords.length > MAX_SYNC_BATCH_SIZE) {\n    // Chunk and enqueue continuation jobs\n    const chunks = chunk(rawRecords, MAX_SYNC_BATCH_SIZE);\n    for (const chunk of chunks.slice(1)) {\n      await enqueueToConnectorSyncQueue({\n        ...job,\n        id: generateJobId(),\n        continuation: true,\n        records: chunk,\n      });\n    }\n  }\n\n  // 6. Enqueue to Normalizer (never direct Pipeline write)\n  await enqueueToNormalizerQueue({\n    tenantId: job.tenantId,\n    provider: config.provider,\n    connectorId: job.connectorId,\n    records: rawRecords,\n    syncJobId: job.id,\n    timestamp: Date.now(),\n  });\n\n  return { recordsFetched: rawRecords.length, status: \"normalized\" };\n}\n```\n\n### 5.3.3 The Loader (Cold-Start & Webhook Trigger)\n\nThe `loader` service is the **on-ramp** for new tenants and real-time events.\n\n```typescript\n// services/loader/src/index.ts\n\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; // Nango webhook signature\n  idempotencyKey: string; // Prevent duplicate processing\n}\n\nexport async function handleLoaderEvent(event: LoaderEvent): Promise<void> {\n  // 1. Verify webhook signature (P0 security)\n  if (event.signature && !verifyNangoSignature(event.payload, event.signature)) {\n    await auditLog({\n      tenantId: event.tenantId,\n      action: \"webhook_rejected\",\n      via: \"loader\",\n      reason: \"bad_signature\",\n    });\n    throw new LoaderError(\"INVALID_SIGNATURE\", 401);\n  }\n\n  // 2. Deduplicate via idempotency key\n  const seen = await kv.get(`idempotency:${event.idempotencyKey}`);\n  if (seen) return;\n  await kv.put(`idempotency:${event.idempotencyKey}`, \"1\", { expirationTtl: 86400 });\n\n  // 3. For auth.created — trigger \"creamy\" (full) sync immediately\n  if (event.type === \"auth.created\") {\n    await updateTenantConnectorStatus(event.tenantId, event.provider, \"connected\");\n    await enqueueToConnectorSyncQueue({\n      tenantId: event.tenantId,\n      connectorId: event.provider,\n      provider: event.provider,\n      syncType: \"creamy\",\n      triggeredBy: \"loader\",\n      since: new Date(0), // Full historical sync\n    });\n    return;\n  }\n\n  // 4. For webhook.received — trigger incremental sync\n  if (event.type === \"webhook.received\") {\n    await enqueueToConnectorSyncQueue({\n      tenantId: event.tenantId,\n      connectorId: event.provider,\n      provider: event.provider,\n      syncType: \"incremental\",\n      triggeredBy: \"webhook\",\n      since: await getLastSyncTime(event.tenantId, event.provider),\n    });\n    return;\n  }\n}\n```\n\n**Queues involved:**\n\n| Queue                  | Producer                          | Consumer         | Message Type           | Delivery                   |\n| ---------------------- | --------------------------------- | ---------------- | ---------------------- | -------------------------- |\n| `CONNECTOR_SYNC_QUEUE` | `loader`, cron trigger, 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---\n\n## 5.4 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### 5.4.1 Normalizer Stages\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         NORMALIZER PIPELINE (NA0–NA7)                       │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│  Raw 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│                                                                             │\n│  Stage 4 ──► Stage 5 ──► Stage 6 ──► Stage 7 ──► PIPELINE_QUEUE             │\n│  (NA4)       (NA5)       (NA6)       (NA7)                                 │\n│  Enrich      Validate    Emit        DLQ / Retry                            │\n│  & Map       Canonical   Canonical                                           │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\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 is optional)                 |\n| **NA6** | Validate       | Schema validation against Schema Provider contract               | Reject + audit log (do NOT DLQ schema violations) |\n| **NA7** | Emit Canonical | Serialize to `CanonicalEntityBatch`, enqueue to `PIPELINE_QUEUE` | Retry ×5 → DLQ (critical path)                    |\n\n### 5.4.2 Normalizer Interfaces\n\n```typescript\n// services/normalizer/src/types.ts\n\nexport interface NormalizationBatch {\n  id: string;\n  tenantId: string;\n  provider: string;\n  connectorId: string;\n  syncJobId: string;\n  records: RawRecord[];\n  receivedAt: number;\n}\n\nexport interface CanonicalEntity {\n  ssocId: string; // Stable UUID\n  tenantId: string;\n  entityType: string;\n  sourceId: string;\n  sourceProvider: string;\n  canonicalPayload: Record<string, unknown>;\n  traits: Trait[];\n  relations: CanonicalRelation[];\n  confidence: number; // Overall confidence 0.0–1.0\n  provenance: ProvenanceChain;\n}\n\nexport interface CanonicalRelation {\n  targetSsocId: string;\n  relationType: string;\n  confidence: number;\n  provenance: \"inferred\" | \"declared\" | \"synced\";\n}\n\nexport interface ProvenanceChain {\n  syncJobId: string;\n  normalizerBatchId: string;\n  stages: { stage: string; timestamp: number; status: \"ok\" | \"warn\" | \"fail\" }[];\n}\n\nexport interface NormalizerState {\n  tenantId: string;\n  lastProcessedAt: number;\n  stageLatencies: Record<string, number>; // p99 per stage\n  dlqCount: number;\n  backpressure: boolean; // If true, pause ingress\n}\n```\n\n### 5.4.3 DLQ & Retry Policy\n\n```typescript\n// services/normalizer/src/dlq.ts\n\n  maxRetries: 5,\n  backoff: \"exponential\", // 1s, 2s, 4s, 8s, 16s\n  maxDelayMs: 30000,\n  retryableErrors: [\"NETWORK_TIMEOUT\", \"RATE_LIMITED\", \"SCHEMA_PROVIDER_UNAVAILABLE\"],\n  nonRetryableErrors: [\n    \"SCHEMA_VIOLATION\", // Bad data → reject, don't retry\n    \"CROSS_TENANT_RECORD\", // Security violation → reject + alert\n    \"INVALID_TRAIT\", // Data quality issue → reject\n  ],\n};\n\nexport async function handleDLQ(batch: NormalizationBatch, error: NormalizerError): Promise<void> {\n  // 1. Write to R2 cold storage for compliance\n  await r2.put(`dlq/${batch.tenantId}/${batch.id}.json`, JSON.stringify({ batch, error }));\n\n  // 2. Audit log\n  await auditLog({\n    tenantId: batch.tenantId,\n    action: \"normalizer_dlq\",\n    via: \"normalizer\",\n    payloadHash: hash(batch),\n    errorCode: error.code,\n  });\n\n  // 3. If cross-tenant, alert security\n  if (error.code === \"CROSS_TENANT_RECORD\") {\n    await alertSecurity({\n      tenantId: batch.tenantId,\n      severity: \"critical\",\n      message: \"Cross-tenant data detected in normalizer\",\n    });\n  }\n}\n```\n\n**Performance SLO:** Normalizer p99 latency < 2s per batch of ≤100 records.\n\n---\n\n## 5.5 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### 5.5.1 Schema Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         SCHEMA PROVIDER ARCHITECTURE                        │\n│                                                                             │\n│   ┌─────────────────┐                                                       │\n│   │  Schema AI      │  (gpt-5-mini, 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│   Ownership Split:                                                          │\n│   • Base Schema = IntegrateWise IP (proprietary, not exposed)              │\n│   • Tenant Overlay = Customer context (data sovereignty)                   │\n│   • Provider Map = Adapter configuration (swappable)                       │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.5.2 Schema Interfaces\n\n```typescript\n// packages/spine-schema/src/schema/schema-provider.ts\n\nexport interface BaseSchema {\n  version: string; // Semver\n  entityTypes: EntityTypeDef[];\n  relationTypes: RelationTypeDef[];\n  validationRules: ValidationRule[];\n}\n\nexport interface EntityTypeDef {\n  name: string; // e.g., 'account', 'contact', 'opportunity'\n  fields: FieldDef[];\n  requiredTraits: string[];\n  displayTemplate: string; // e.g., \"{{name}} ({{industry}})\"\n  // IntegrateWise-owned; never exposed to tenant\n}\n\nexport interface TenantOverlay {\n  tenantId: string;\n  customFields: CustomFieldDef[];\n  fieldMappings: FieldMapping[]; // Map provider field → canonical field\n  disabledEntityTypes: string[];\n  // Customer-owned; data sovereignty applies\n}\n\nexport interface ProviderMap {\n  provider: string;\n  entityMappings: Record<string, string>; // provider type → canonical type\n  fieldMappings: Record<string, string>; // provider field → canonical field\n  // Swappable; changes when provider changes\n}\n\nexport interface SchemaProviderAPI {\n  // Returns the merged schema (Base + Tenant Overlay) for a tenant\n  getTenantSchema(tenantId: string): Promise<MergedSchema>;\n\n  // Returns the provider-specific mapping for a connector\n  getProviderMap(tenantId: string, provider: string): Promise<ProviderMap>;\n\n  // Validates a canonical payload against the merged schema\n  validate(tenantId: string, entityType: string, payload: unknown): Promise<ValidationResult>;\n\n  // Called by Schema AI on tenant onboarding\n  generateInitialSchema(\n    tenantId: string,\n    industry: string,\n    connectedProviders: string[]\n  ): Promise<BaseSchema>;\n}\n```\n\n---\n\n## 5.6 Workspace\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### 5.6.1 Workspace Boundaries\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                        WORKSPACE (Tenant-Scoped Boundary)                   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   Workspace = Tenant = Org                                                  │\n│                                                                             │\n│   ┌─────────────────────────────────────────┐                               │\n│   │  Tenant: acme-corp                      │                               │\n│   │  ─────────────────────────────────────  │                               │\n│   │  • Spine entities (WHERE tenant_id=?)   │                               │\n│   │  • Memory (Personal, Work, Org, AI)     │                               │\n│   │  • Knowledge (RAG corpus, tenant-scoped)│                               │\n│   │  • Governance rules (tenant-specific)   │                               │\n│   │  • Audit logs (tenant-scoped)           │                               │\n│   │  • Connectors (per-tenant credentials)  │                               │\n│   │  • Projections (8 lenses)               │                               │\n│   └─────────────────────────────────────────┘                               │\n│                                                                             │\n│   HARD RULES:                                                               │\n│   1. No cross-tenant query ever succeeds.                                   │\n│   2. Every Durable Object ID includes tenant_id.                            │\n│   3. Every queue message carries tenant_id in the envelope.                 │\n│   4. Every JWT claim includes tenant_id.                                    │\n│   5. Service bindings propagate tenant_id implicitly.                       │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.6.2 Workspace Initialization (First Auth Flow)\n\n```typescript\n// services/tenants/src/onboarding.ts\n\nexport async function initializeWorkspace(\n  tenantId: string,\n  ownerEmail: string,\n  industry: string\n): Promise<Workspace> {\n  // 1. Create D1 partition (tenant-scoped tables are already isolated by tenant_id)\n  //    No physical partition needed — logical isolation via WHERE clause.\n\n  // 2. Generate initial schema via Schema AI\n  const baseSchema = await schemaProvider.generateInitialSchema(tenantId, industry, []);\n\n  // 3. Create RBAC roles\n  await createRole(tenantId, \"owner\", OWNER_PERMISSIONS);\n  await createRole(tenantId, \"tam\", TAM_PERMISSIONS);\n  await createRole(tenantId, \"account_success\", CS_PERMISSIONS);\n\n  // 4. Initialize memory tiers\n  await continuity.initMemoryForTenant(tenantId);\n\n  // 5. Initialize Knowledge (empty RAG corpus)\n  await knowledge.initKnowledgeForTenant(tenantId);\n\n  // 6. Set default governance rules\n  await govern.setDefaultRules(tenantId);\n\n  // 7. Audit log\n  await auditLog({ tenantId, action: \"workspace_initialized\", actor: `user:${ownerEmail}` });\n\n  return { tenantId, status: \"active\", createdAt: new Date() };\n}\n```\n\n---\n\n## 5.7 Memory (The Continuity Substrate)\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### 5.7.1 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### 5.7.2 Memory Tiers\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                           MEMORY LIFECYCLE                                   │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   Intake ──► Classification ──► Validation ──► Promotion ──► Store          │\n│      │            │                │              │            │             │\n│      │            ▼                │              │            ▼             │\n│      │    Confidence Score         │              │      ┌──────────┐        │\n│      │    (0.0 – 1.0)              │              │      │  ACTIVE  │        │\n│      │                             │              │      │  (D1 +   │        │\n│      │            ┌────────────────┘              │      │   KV +   │        │\n│      │            ▼                                │      │ Vectorize│        │\n│      │    ┌───────────────┐    ≥0.85 ────────────┘      └──────────┘        │\n│      └───►│   TRIAGE      │◄────────────────────────────────│               │\n│           │  (triage      │                                 │ reinforcement │\n│           │   service)    │                                 │ resets clock  │\n│           └───────┬───────┘                                 │               │\n│                   │                                         ▼               │\n│         0.70–0.85 │                                ┌──────────┐             │\n│                   └───────────────────────────────►│ STAGING  │             │\n│                                                    │  (D1)    │             │\n│         <0.70                                      └──────────┘             │\n│            │                                          │ 30 days no access   │\n│            ▼                                          ▼                     │\n│      ┌──────────┐                            ┌──────────┐                   │\n│      │  DISCARD │                            │ ARCHIVED │                   │\n│      │  (R2     │                            │  (R2)    │                   │\n│      │  audit)  │                            │  cold    │                   │\n│      └──────────┘                            └──────────┘                   │\n│                                                                             │\n│   DECAY RULE:                                                               │\n│   • Active memory decays after ~30 days of no access or reinforcement.     │\n│   • Decay is time-based + access-based.                                     │\n│   • Reinforcement (read, update, Twin reference) resets the decay clock.   │\n│   • Archived memory can be reactivated by query (with latency penalty).    │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.7.3 Memory Interfaces\n\n```typescript\n// packages/twin-types/src/memory.ts\n\nexport type MemoryScope = \"personal\" | \"work\" | \"organization\" | \"ai\";\n\nexport type MemoryTier = \"active\" | \"staging\" | \"archived\";\n\nexport interface MemoryRecord {\n  id: string;\n  tenantId: string;\n  scope: MemoryScope;\n  ownerId: string; // user_id for personal, task_id for work, etc.\n  tier: MemoryTier;\n  content: string; // Text content or serialized JSON\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 in this memory\n  accessCount: number;\n  lastAccessedAt?: Date;\n  createdAt: Date;\n  decayAt?: Date; // When this record will auto-archive\n}\n\nexport interface MemoryQuery {\n  tenantId: string;\n  scope?: MemoryScope;\n  ownerId?: string;\n  semanticQuery?: string; // Natural language query → embedding search\n  ssocFilter?: string[]; // Filter to memories referencing these entities\n  recencyWeight?: number; // 0.0–1.0, default 0.5\n  relevanceWeight?: number; // 0.0–1.0, default 0.5\n  limit: number;\n}\n\nexport interface MemoryStoreAPI {\n  // Store a new memory (post-triage)\n  store(record: Omit<MemoryRecord, \"id\">): Promise<MemoryRecord>;\n\n  // Query memories (semantic + keyword hybrid)\n  query(q: MemoryQuery): Promise<MemoryRecord[]>;\n\n  // Reinforce (reset decay clock)\n  reinforce(memoryId: string): Promise<void>;\n\n  // Decay runner (called by `continuity` service on schedule)\n  runDecay(tenantId: string): Promise<{ archived: number; decayed: number }>;\n\n  // Cross-scope promotion (e.g., personal insight → org knowledge)\n  promote(\n    memoryId: string,\n    fromScope: MemoryScope,\n    toScope: MemoryScope,\n    approvedBy: string\n  ): Promise<void>;\n}\n```\n\n### 5.7.4 Continuity Graph\n\nThe Continuity Graph is the **relationship traversal layer** of memory. It answers: \"Who/what/when/where is connected to this entity?\"\n\n```typescript\n// services/continuity/src/graph.ts\n\nexport interface ContinuityGraphAPI {\n  // Walk the graph from a starting entity\n  traverse(\n    tenantId: string,\n    startSsocId: string,\n    options: {\n      depth: number; // Max traversal depth (default 3)\n      relationTypes?: string[]; // Filter to these relation types\n      minConfidence?: number; // Filter edges ≥ this confidence\n      direction?: \"out\" | \"in\" | \"both\";\n    }\n  ): Promise<GraphWalkResult>;\n\n  // Compute \"Entity360\" — the full context assembly for an entity\n  getEntity360(tenantId: string, ssocId: string): Promise<Entity360>;\n}\n\nexport interface Entity360 {\n  entity: CanonicalEntity;\n  directRelations: RelatedEntity[];\n  indirectRelations: RelatedEntity[];\n  recentMemory: MemoryRecord[];\n  timeline: TimelineEvent[];\n  sentiment?: number; // Computed by intelligence service\n  healthScore?: number; // Computed by intelligence service\n}\n```\n\n---\n\n## 5.8 Knowledge (RAG & Semantic Search)\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### 5.8.1 Knowledge Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         KNOWLEDGE ARCHITECTURE (RAG)                        │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   Documents ──► Chunking ──► Embedding ──► Vectorize ──► Retrieval          │\n│      │            │            │             │              │               │\n│      │            │            │             │              ▼               │\n│      │            │            │             │      ┌───────────────┐       │\n│      │            │            │             │      │  AI Search    │       │\n│      │            │            │             │      │  (semantic)   │       │\n│      │            │            │             │      └───────────────┘       │\n│      │            │            │             │              │               │\n│      ▼            ▼            ▼             ▼              ▼               │\n│   ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐  ┌───────────────┐       │\n│   │  R2    │  │Chunking│  │Embedding│  │Vectorize│  │  Context     │       │\n│   │(blobs) │  │(think) │  │(think) │  │(index) │  │  Assembly    │       │\n│   └────────┘  └────────┘  └────────┘  └────────┘  └───────────────┘       │\n│                                                                             │\n│   Chunking Strategy:                                                        │\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└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.8.2 Knowledge Interfaces\n\n```typescript\n// services/knowledge/src/types.ts\n\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: {\n    heading?: string;\n    pageNumber?: number;\n  };\n}\n\nexport interface KnowledgeQuery {\n  tenantId: string;\n  query: string; // Natural language\n  filters?: {\n    source?: string[];\n    createdAfter?: Date;\n    createdBy?: string;\n  };\n  topK: number; // Number of chunks to return\n  minScore?: number; // Minimum relevance score\n}\n\nexport interface KnowledgeAPI {\n  ingest(doc: Omit<KnowledgeDocument, \"id\" | \"chunks\">): Promise<KnowledgeDocument>;\n  query(q: KnowledgeQuery): Promise<KnowledgeChunk[]>;\n  reindex(tenantId: string, documentId: string): Promise<void>;\n  delete(tenantId: string, documentId: string): Promise<void>;\n}\n```\n\n---\n\n## 5.9 Governance (The Airlock)\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### 5.9.1 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### 5.9.2 Governance Rules Engine\n\n```typescript\n// services/govern/src/rules.ts\n\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 the default 0.70/0.85 for this rule\n  expiresAt?: Date;\n  createdBy: string;\n  createdAt: Date;\n}\n\nexport interface RuleCondition {\n  type: \"confidence_threshold\" | \"field_match\" | \"role_based\" | \"time_based\" | \"composite\";\n  config: Record<string, unknown>;\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  // Score a proposal and return the governance decision\n  evaluate(proposal: ActionProposal): Promise<GovernanceDecision>;\n\n  // Create/update/delete rules (admin only)\n  setRule(rule: GovernanceRule): Promise<void>;\n  deleteRule(tenantId: string, ruleId: string): Promise<void>;\n\n  // Query pending proposals for My Desk\n  getPendingProposals(tenantId: string, userId: string): Promise<ActionProposal[]>;\n\n  // Approve/reject/dismiss a pending proposal\n  resolveProposal(\n    proposalId: string,\n    resolution: \"approve\" | \"reject\" | \"modify\",\n    resolverId: string,\n    modifiedPayload?: unknown\n  ): Promise<void>;\n}\n```\n\n### 5.9.3 HITL Orchestrator (Human-in-the-Loop)\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```typescript\n// services/govern/src/hitl-do.ts\n\nexport class HITLOrchestrator implements DurableObject {\n  private state: DurableObjectState;\n  private tenantId: string;\n  private userId: string;\n\n  constructor(state: DurableObjectState, env: Env) {\n    this.state = state;\n    // CRITICAL: DO ID must be idFromName(tenant_id + user_id), NEVER \"global\"\n    const id = state.id.toString();\n    // Extract tenant_id and user_id from the ID (or from storage init)\n  }\n\n  async fetch(request: Request): Promise<Response> {\n    const url = new URL(request.url);\n    const path = url.pathname;\n\n    if (path === \"/proposals\") {\n      return this.listProposals();\n    }\n    if (path === \"/proposals/:id/approve\") {\n      return this.approveProposal(url.searchParams.get(\"id\")!);\n    }\n    if (path === \"/proposals/:id/reject\") {\n      return this.rejectProposal(url.searchParams.get(\"id\")!);\n    }\n    if (path === \"/proposals/:id/modify\") {\n      return this.modifyProposal(url.searchParams.get(\"id\")!, await request.json());\n    }\n\n    return new Response(\"Not Found\", { status: 404 });\n  }\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\n    // 2. Set alarm for expiry (72 hours)\n    await this.state.storage.setAlarm(Date.now() + 72 * 60 * 60 * 1000);\n\n    // 3. Notify user (SSE push, email, or Slack — configured per tenant)\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    const now = Date.now();\n    for (const [key, proposal] of proposals) {\n      if (proposal.status === \"pending\" && 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### 5.9.4 Governance Flow\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         GOVERNANCE FLOW (Decide → Act)                      │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   ┌───────────────┐                                                         │\n│   │  Twin/Agent   │                                                         │\n│   │  (Reasoning)  │                                                         │\n│   └───────┬───────┘                                                         │\n│           │ ActionProposal                                                  │\n│           ▼                                                                 │\n│   ┌───────────────┐     ┌─────────────────────────────────────────────┐    │\n│   │    GOVERN     │────►│  Confidence Score (0.0 – 1.0)               │    │\n│   │  (govern      │     │  • Twin reasoning quality                     │    │\n│   │   service)    │     │  • Historical success rate                    │    │\n│   │               │     │  • Rule match severity                        │    │\n│   │  Rules Engine │     │  • Data freshness                             │    │\n│   │  + HITL DO    │     └─────────────────────────────────────────────┘    │\n│   └───────┬───────┘                                                         │\n│           │                                                                 │\n│     ┌─────┼─────┐                                                           │\n│     │     │     │                                                           │\n│     ▼     ▼     ▼                                                           │\n│  ┌────┐ ┌────┐ ┌────┐                                                       │\n│  │≥0.85│ │0.70│ │<0.70│                                                      │\n│  │     │ │–   │ │      │                                                      │\n│  │AUTO │ │0.85│ │DIS-  │                                                      │\n│  │APPROVE│ │    │ │CARD  │                                                      │\n│  └──┬──┘ └──┬─┘ └────┘                                                       │\n│     │       │                                                               │\n│     ▼       ▼                                                               │\n│  ┌──────┐ ┌──────────┐                                                      │\n│  │ ACT  │ │ MY DESK  │                                                      │\n│  │QUEUE │ │(HITL DO) │                                                      │\n│  │      │ │          │                                                      │\n│  │ act  │ │ User     │                                                      │\n│  │service│ │ reviews  │                                                      │\n│  └──────┘ │ Approve  │                                                      │\n│           │ Modify   │                                                      │\n│           │ Dismiss  │                                                      │\n│           └────┬─────┘                                                      │\n│                │                                                            │\n│                ▼ (on approve)                                               │\n│           ┌──────────┐                                                      │\n│           │  ACT     │                                                      │\n│           │  QUEUE   │                                                      │\n│           └──────────┘                                                      │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## 5.10 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### 5.10.1 Audit Tables\n\n| Table                  | What It Logs                                       | Failure Behavior               | Retention                  |\n| ---------------------- | -------------------------------------------------- | ------------------------------ | -------------------------- |\n| `audit_logs`           | Generic platform events                            | Write failure → halt operation | 90 days (R2 archive after) |\n| `governance_audit_log` | Approval decisions (approve/reject/discard/expire) | Write failure → halt proposal  | 7 years                    |\n| `spine_audit_log`      | Entity changes, MCP access, reads, writes          | Write failure → halt sync      | 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### 5.10.2 Audit Interface\n\n```typescript\n// packages/lib/src/audit.ts\n\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  // Compliance fields\n  gdprCategory?: \"personal_data\" | \"sensitive_data\" | \"anonymized\";\n  dataSubjectId?: string; // For GDPR subject access requests\n  retentionPolicy: \"standard\" | \"extended\" | \"permanent\";\n}\n\nexport interface AuditAPI {\n  // Append-only write\n  log(event: Omit<AuditEvent, \"id\" | \"timestamp\">): Promise<void>;\n\n  // Query (tenant-scoped, time-bounded)\n  query(tenantId: string, since: Date, until: Date, filters?: AuditFilter): Promise<AuditEvent[]>;\n\n  // Export for compliance (GDPR Right to Access, SOC2)\n  export(tenantId: string, format: \"json\" | \"csv\"): Promise<ReadableStream>;\n\n  // GDPR Right to Erasure (anonymize, do NOT delete — audit must remain)\n  anonymize(tenantId: string, dataSubjectId: string): Promise<void>;\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 = {\n    ...event,\n    id: generateUlid(),\n    timestamp: new Date(),\n  };\n\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### 5.10.3 Compliance Boundaries\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                       COMPLIANCE BOUNDARIES                                  │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│   ┌─────────────────────────────────────────────────────────────────────┐   │\n│   │  TENANT ISOLATION                                                   │   │\n│   │  • Every query: WHERE tenant_id = ?                                 │   │\n│   │  • Cross-tenant access: 403 (fail-loud, not 404)                   │   │\n│   │  • Nango credentials: connectionId = tenant_id, encrypted vault    │   │\n│   │  • Durable Objects: idFromName(tenant_id + suffix)                 │   │\n│   └─────────────────────────────────────────────────────────────────────┘   │\n│                                                                             │\n│   ┌─────────────────────────────────────────────────────────────────────┐   │\n│   │  DATA RESIDENCY                                                     │   │\n│   │  • Default: Cloudflare data centers (global edge)                   │   │\n│   │  • Enterprise: Pin to specific jurisdictions (EU, US, AU)           │   │\n│   │  • R2 buckets: Region-pinned per tenant config                      │   │\n│   └─────────────────────────────────────────────────────────────────────┘   │\n│                                                                             │\n│   ┌─────────────────────────────────────────────────────────────────────┐   │\n│   │  ENCRYPTION                                                         │   │\n│   │  • At-rest: D1 encrypted by Cloudflare                              │   │\n│   │  • In-transit: TLS 1.3 (Cloudflare proxy)                           │   │\n│   │  • Credentials: Cloudflare Secrets (never in code/env)              │   │\n│   │  • PII in memory: Hashed or tokenized where possible                │   │\n│   └─────────────────────────────────────────────────────────────────────┘   │\n│                                                                             │\n│   ┌─────────────────────────────────────────────────────────────────────┐   │\n│   │  RETENTION                                                          │   │\n│   │  • Active memory: 30 days (with reinforcement reset)                │   │\n│   │  • Audit logs: 7 years (compliance)                                 │   │\n│   │  • Spine history: Permanent (soft-delete only)                      │   │\n│   │  • R2 cold storage: Configurable per tenant plan                    │   │\n│   │  • GDPR erasure: Anonymize audit subjects; keep audit trail         │   │\n│   └─────────────────────────────────────────────────────────────────────┘   │\n│                                                                             │\n│   ┌─────────────────────────────────────────────────────────────────────┐   │\n│   │  ACCESS LOGGING                                                     │   │\n│   │  • Every MCP invocation: logged to spine_audit_log with via:mcp     │   │\n│   │  • Every governance decision: logged to governance_audit_log        │   │\n│   │  • Every sync: logged with record counts, latency, errors           │   │\n│   │  • Every Twin reasoning: logged with model, prompt hash, output     │   │\n│   └─────────────────────────────────────────────────────────────────────┘   │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## 5.11 Security Boundaries\n\n### 5.11.1 Trust Boundaries\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                      SECURITY ZONES (Continuity + Governance)               │\n├─────────────────────────────────────────────────────────────────────────────┤\n│                                                                             │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  ZONE 0: UNTRUSTED                                                   │    │\n│  │  • External providers (Salesforce, HubSpot, Slack)                  │    │\n│  │  • AI assistants (ChatGPT, Claude) — via MCP inbound                │    │\n│  │  • User browsers — via Gateway                                      │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n│                                    │                                        │\n│                                    ▼                                        │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  ZONE 1: INGRESS (Gateway, webhook-ingress, MCP inbound)            │    │\n│  │  • JWT validation                                                   │    │\n│  │  • tenant_id extraction from JWT claim (never from payload)         │    │\n│  │  • Rate limiting                                                    │    │\n│  │  • CORS whitelist                                                   │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n│                                    │                                        │\n│                                    ▼                                        │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  ZONE 2: CONTINUITY KERNEL (This domain)                            │    │\n│  │  • Spine (D1) — writer-guarded                                      │    │\n│  │  • Memory (D1 + KV + Vectorize) — tenant-scoped                     │    │\n│  │  • Knowledge (R2 + Vectorize) — tenant-scoped                       │    │\n│  │  • Governance (D1 + DO) — HITL per-tenant+user                      │    │\n│  │  • Audit (D1 + R2) — append-only                                    │    │\n│  │                                                                     │    │\n│  │  RULE: No service in Zone 2 calls Zone 0 directly.                  │    │\n│  │  All outbound calls go through Zone 3 (Provider Fabric).            │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n│                                    │                                        │\n│                                    ▼                                        │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  ZONE 3: PROVIDER FABRIC                                            │    │\n│  │  • Act service + outbound adapters (Nango, MCP, Native)             │    │\n│  │  • Credential wall (Cloudflare Secrets / Nango vault)               │    │\n│  │  • Circuit breakers                                                 │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n│                                    │                                        │\n│                                    ▼                                        │\n│  ┌─────────────────────────────────────────────────────────────────────┐    │\n│  │  ZONE 4: EXTERNAL PROVIDERS                                         │    │\n│  │  • Salesforce, HubSpot, Slack, GitHub, Stripe, Notion, ...          │    │\n│  └─────────────────────────────────────────────────────────────────────┘    │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n### 5.11.2 P0 Security Checklist (Continuity + Governance)\n\n| #   | Control                           | Implementation                                                              | Owner        |\n| --- | --------------------------------- | --------------------------------------------------------------------------- | ------------ |\n| 1   | Cross-tenant query enforcement    | `withTenant(query, tenantId)` in `packages/spine-schema`                    | ALL          |\n| 2   | Spine write isolation             | `pipeline` is the only writer; `db-gate` rejects writes from other services | `pipeline`   |\n| 3   | HITL DO isolation                 | `idFromName(tenant_id + user_id)` — never global                            | `govern`     |\n| 4   | Memory scope enforcement          | `continuity` service validates scope on every read/write                    | `continuity` |\n| 5   | Audit tamper resistance           | Append-only D1 table; no UPDATE or DELETE privileges                        | `pipeline`   |\n| 6   | Normalizer cross-tenant detection | Reject + alert if `tenant_id` in record mismatches batch                    | `normalizer` |\n| 7   | Knowledge tenant isolation        | Vectorize index filtered by `tenant_id` at query time                       | `knowledge`  |\n| 8   | Governance rule integrity         | Rules are tenant-scoped; no global rules that affect all tenants            | `govern`     |\n\n---\n\n## 5.12 Integration Points with Adjacent Layers\n\n### 5.12.1 North-Bound (Capability Layer)\n\n| From                | To             | Interface                      | Data                                    |\n| ------------------- | -------------- | ------------------------------ | --------------------------------------- |\n| `twin-orchestrator` | `govern`       | Service binding `GOVERN`       | `ActionProposal`                        |\n| `intelligence`      | `continuity`   | Service binding `CONTINUITY`   | `MemoryQuery` → `MemoryRecord[]`        |\n| `knowledge`         | `intelligence` | Service binding `INTELLIGENCE` | `KnowledgeChunk[]` for Context Assembly |\n| `l2`                | `pipeline`     | Service binding `PIPELINE`     | `ProjectionBatch` for Spine write       |\n| `store`             | `pipeline`     | Service binding `PIPELINE`     | `EntityCRUD` operations                 |\n\n### 5.12.2 South-Bound (Provider Fabric)\n\n| From             | To           | Interface                | Data                          |\n| ---------------- | ------------ | ------------------------ | ----------------------------- |\n| `govern`         | `hermes`     | Queue `ACT_QUEUE`        | `ApprovedAction`              |\n| `hermes`         | `act`        | Service binding `ACT`    | `ExecutionJob`                |\n| `act`            | `pipeline`   | Queue `PIPELINE_QUEUE`   | `ExecutionResult` (writeback) |\n| `connector-sync` | `normalizer` | Queue `NORMALIZER_QUEUE` | `RawRecord[]`                 |\n| `normalizer`     | `pipeline`   | Queue `PIPELINE_QUEUE`   | `CanonicalEntity[]`           |\n\n### 5.12.3 Cross-Cutting (All Layers)\n\n| Service     | Integration                        | Purpose                                          |\n| ----------- | ---------------------------------- | ------------------------------------------------ |\n| `telemetry` | All 26 services                    | Metrics, traces, logs via Workers Analytics      |\n| `billing`   | `govern`, `pipeline`, `continuity` | Usage metering for actions, storage, model calls |\n| `admin`     | `govern`, `tenants`                | Override governance rules, workspace management  |\n\n---\n\n## 5.13 Service Topology for This Domain\n\nThe Continuity + Governance domain is implemented across **9 of the 26 services**:\n\n| Service             | Tier   | Role in This Domain                            | Binding             |\n| ------------------- | ------ | ---------------------------------------------- | ------------------- |\n| `pipeline`          | Tier 2 | **Sole Spine writer**; audit log writer        | `PIPELINE`          |\n| `normalizer`        | Tier 1 | 8-stage NA0–NA7 pipeline                       | `NORMALIZER`        |\n| `connector-sync`    | Tier 1 | Sync orchestration; inbound adapters           | `CONNECTOR_SYNC`    |\n| `loader`            | Tier 1 | Cold-start; Nango webhook handler              | `LOADER`            |\n| `continuity`        | Tier 4 | Memory consolidation + decay                   | `CONTINUITY`        |\n| `knowledge`         | Tier 2 | RAG, semantic search, document indexing        | `KNOWLEDGE`         |\n| `govern`            | Tier 2 | Governance rules; approval gates; HITL DO      | `GOVERN`            |\n| `intelligence`      | Tier 2 | Context Assembly; Entity360                    | `INTELLIGENCE`      |\n| `twin-orchestrator` | Tier 2 | Persistent per-user reasoning (Durable Object) | `TWIN_ORCHESTRATOR` |\n\n---\n\n## 5.14 Key Invariants\n\n1. **Pipeline is the only writer.** No service other than `pipeline` may `INSERT`, `UPDATE`, or `DELETE` from `entities` or `relationships`.\n2. **Every hop carries `tenant_id`.** From Gateway → service binding → queue → D1 → Durable Object. No exception.\n3. **Cross-tenant access is 403, not 404.** Fail-loud. Alert security.\n4. **Audit write failure halts the operation.** No silent drops.\n5. **HITL DO is per-tenant+user.** Never global. Never shared.\n6. **Memory decays.** Active memory has a ~30-day half-life without reinforcement.\n7. **The 0.70/0.85 law is invariant.** No tenant, not even Customer-Zero, can bypass governance thresholds.\n8. **Spine history is permanent.** Soft-delete only. `archivedAt` is set; the row remains.\n9. **The Twin is ambient.** It is not a service boundary; it is a reasoning quality that runs through `intelligence`, `continuity`, and `knowledge`.\n10. **Ecosystem is the capability fabric.** Knowledge sharing, tool-to-tool communication, and agent communication happen through the Spine and Memory — never peer-to-peer.\n\n---\n\n## 5.15 Glossary\n\n| Term                | Definition                                                                                             |\n| ------------------- | ------------------------------------------------------------------------------------------------------ |\n| **Spine**           | The directed property graph of all canonical entities and relationships. The single source of context. |\n| **SSOC**            | Stable Source-Object Correlation. A UUID that survives provider swaps.                                 |\n| **Normalizer**      | The 8-stage pipeline that transforms raw provider records into canonical Spine entities.               |\n| **Creamy Sync**     | A full historical sync triggered on first auth (as opposed to incremental).                            |\n| **My Desk**         | The human approval interface for proposals in the 0.70–0.85 confidence band.                           |\n| **HITL**            | Human-in-the-Loop. The orchestration of human approval within automated workflows.                     |\n| **Triage**          | The confidence-scoring and routing logic for both execution and memory.                                |\n| **Entity360**       | The assembled context for a single entity: its properties, relations, memory, and timeline.            |\n| **OODA**            | Observe → Orient → Decide → Act. The decision cycle that runs across Capability and Continuity.        |\n| **Provider Fabric** | The swappable layer of external SaaS providers, APIs, and runtimes.                                    |\n\n---\n\n_End of §5 — Continuity Layer & Governance. This section is a Stage 1/2 deep-dive for the IntegrateWise Continuity Bridge v2.0 Canonical Architecture. It will be merged into the final canonical document after review._\n"}