{"slug":"internal/architecture/ARCHITECTURE_COMPLETE","title":"IntegrateWise Complete Architecture - Tasks 1-3 Built & Documented","section":"internal","sectionLabel":"Internal","status":"unclassified","type":"document","path":"internal/architecture/ARCHITECTURE_COMPLETE.mdx","bytes":29698,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n\n> **Tier C — Historical.** This document is a point-in-time snapshot preserved for reference. For current architecture, see [CANONICAL_PLATFORM_ARCHITECTURE.md](../architecture/CANONICAL_PLATFORM_ARCHITECTURE.md).\n\n## High-Level Data Flow (End-to-End)\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ NEW USER JOURNEY: Auth → Onboarding → Workspace                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n\n1. OAuth Callback (Gateway)\n   ├─ User → app.integratewise.ai\n   ├─ Clicks \"Sign in with Google\"\n   ├─ OAuth callback → gateway/auth.ts\n   ├─ Call: getOrCreateUser(email, name, tenantId)\n   └─ Check: onboarding_completed_at?\n\n2. Onboarding (if NEW user)\n   ├─ Redirect to /onboarding\n   ├─ Step 1: Select role (10 options)\n   │  └─ role-selector.tsx renders roles\n   ├─ Step 2: Select connectors (5-7 per role)\n   │  └─ connector-selector.tsx shows suggested\n   ├─ Step 3: Review selections\n   │  └─ onboarding-wizard.tsx handles flow\n   ├─ POST /api/v1/onboarding/complete\n   │  ├─ Store: users.role + users.onboarding_completed_at\n   │  ├─ Store: tenant_spine_config.connected_connectors\n   │  ├─ Issue: MCP token (15 min HMAC)\n   │  └─ Return: redirect URL\n   └─ sessionStorage.setItem('mcp_token', token)\n\n3. Workspace Load (Existing or Post-Onboarding)\n   ├─ Redirect to /workspace/{domain}/{view}\n   ├─ L1Provider generates projection for role\n   ├─ Normalizer queries Spine via MCP tool\n   │  ├─ Header: Authorization: Bearer {mcp_token}\n   │  ├─ MCP Server: validateMcpToken()\n   │  ├─ MCP Server: resolveActor() from D1\n   │  ├─ MCP Server: validateActorCanAccessTool()\n   │  └─ Query: encrypted vault\n   ├─ Dashboard renders with real metrics\n   │  ├─ Metrics: active_accounts, pipeline_value, mrr_total\n   │  ├─ Widgets: account health, deal stages\n   │  └─ Actions: linked to domain wiring\n   └─ User sees hydrated workspace\n\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ CONNECTOR DATA FLOW: Live Spine Hydration                                   │\n└─────────────────────────────────────────────────────────────────────────────┘\n\n4. Connectors (Freshsales + Razorpay)\n   ├─ FreshsalesConnector\n   │  ├─ runFullSync()\n   │  ├─ syncContacts() → API paginated requests\n   │  │  ├─ Map: Freshsales contact → Spine Contact\n   │  │  ├─ Merge fields: name, email, owner, engagement_level\n   │  │  └─ Emit: SpineEmitter.emit(...)\n   │  ├─ syncAccounts() → API paginated requests\n   │  │  ├─ Map: Freshsales account → Spine Account\n   │  │  ├─ Merge fields: name, industry, revenue, health_score\n   │  │  └─ Emit: SpineEmitter.emit(...)\n   │  └─ syncDeals() → API paginated requests\n   │     ├─ Map: Freshsales deal → Spine Opportunity\n   │     ├─ Merge fields: name, stage, value, probability, age_days\n   │     └─ Emit: SpineEmitter.emit(...)\n   │\n   └─ RazorpayConnector\n      ├─ runFullSync()\n      ├─ syncCustomers() → API paginated requests\n      │  ├─ Map: Razorpay customer → Spine Account\n      │  ├─ Merge fields: name, email, gstin\n      │  └─ Emit: SpineEmitter.emit(...)\n      ├─ syncInvoices() → API paginated requests\n      │  ├─ Map: Razorpay invoice → Spine Invoice\n      │  ├─ Merge fields: amount, status, days_overdue\n      │  └─ Emit: SpineEmitter.emit(...)\n      └─ syncSubscriptions() → API paginated requests\n         ├─ Map: Razorpay subscription → Spine Subscription\n         ├─ Merge fields: plan_id, status, mrr, renewal_date\n         └─ Emit: SpineEmitter.emit(...)\n\n5. Spine Service (receives events)\n   ├─ Validate: normalized event schema\n   ├─ Check: idempotency_key (deduplicate)\n   ├─ Encrypt: AES-256 (data at rest)\n   ├─ Store: D1 vault\n   ├─ Index: Vectorize (semantic search)\n   └─ Update: last_sync_time\n\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ CHATGPT INTEGRATION: MCP Server with Auth                                   │\n└─────────────────────────────────────────────────────────────────────────────┘\n\n6. ChatGPT Enterprise\n   ├─ Settings → Connectors → IntegrateWise Spine\n   ├─ OAuth → get MCP token (same token format)\n   └─ Ask question: \"What accounts are at risk?\"\n\n7. MCP Server (authenticated)\n   ├─ Receive: POST /call\n   │  └─ Authorization: Bearer {token}\n   │\n   ├─ Auth Middleware (mcp-connector/src/middleware/auth.ts)\n   │  ├─ Parse: \"Bearer {payload_b64}.{sig_b64}\"\n   │  ├─ Verify: HMAC-SHA256({payload_b64}, secret)\n   │  ├─ Check: expiration (now < exp)\n   │  ├─ Extract: tenant_id, user_id\n   │  └─ Result: { success: true, actor }\n   │\n   ├─ Actor Resolver (mcp-connector/src/middleware/actor.ts)\n   │  ├─ Query D1: users table\n   │  │  └─ Get: email, name, role\n   │  ├─ Query D1: tenant_spine_config\n   │  │  └─ Get: tier, connected_connectors, allowed_entity_types\n   │  ├─ Derive: permissions from role + tier\n   │  │  ├─ admin: all permissions\n   │  │  ├─ manager: read + write + report + user management\n   │  │  └─ user: read + execute\n   │  │  ├─ free: spine tools only\n   │  │  ├─ starter: + memory tools\n   │  │  ├─ pro: + advanced features\n   │  │  └─ enterprise: all features\n   │  └─ Result: full actor context\n   │\n   ├─ Permission Gate\n   │  ├─ Check: actor.permissions includes \"spine:query\"?\n   │  ├─ Check: entity_type in allowed_entity_types?\n   │  ├─ Check: connector in connected_sources?\n   │  └─ Result: allow or deny with reason\n   │\n   ├─ Tool Dispatch (dispatchSpineTool)\n   │  ├─ Tool: spine.query\n   │  │  └─ Query: vault for entities matching filters\n   │  ├─ Tool: spine.get\n   │  │  └─ Get: single entity by source_id\n   │  ├─ Tool: memory.search\n   │  │  └─ Semantic search via Vectorize\n   │  ├─ Tool: memory.propose\n   │  │  └─ Submit to proposals table (triage bot queue)\n   │  └─ Tool: spine.connected_sources\n   │     └─ List: connected connectors for tenant\n   │\n   └─ Response: JSON with canonical entities\n      └─ ChatGPT generates: natural language response\n\n```\n\n---\n\n## Component Architecture (What's Built)\n\n```\n┌──────────────────────────────────────────────────────────────────────────────┐\n│ LAYER 1: Frontend (React / Next.js)                                          │\n├──────────────────────────────────────────────────────────────────────────────┤\n│\n├─ Onboarding Components (TASK 1 - BUILT)\n│  ├─ apps/web/app/onboarding/page.tsx\n│  │  └─ Entry point for new users\n│  │\n│  └─ apps/web/components/onboarding/\n│     ├─ onboarding-wizard.tsx\n│     │  ├─ Manages 3-step flow\n│     │  ├─ Calls API endpoints\n│     │  ├─ Stores MCP token in session\n│     │  └─ Handles completion + redirect\n│     │\n│     ├─ role-selector.tsx\n│     │  ├─ Grid of 10 role cards\n│     │  ├─ Click → select → step 2\n│     │  └─ Shows suggested connectors\n│     │\n│     └─ connector-selector.tsx\n│        ├─ Multi-select checkboxes\n│        ├─ Shows suggested connectors first\n│        ├─ \"Select All\" button\n│        └─ Continue button\n│\n├─ API Routes (TASK 1 - BUILT)\n│  └─ apps/web/app/api/v1/onboarding/\n│     ├─ start/route.ts\n│     │  ├─ Check if user is new\n│     │  └─ Return wizard config\n│     │\n│     └─ complete/route.ts\n│        ├─ Validate role + connectors\n│        ├─ Call gateway to issue MCP token (TODO)\n│        ├─ Return token + redirect URL\n│        └─ Frontend stores token in session\n│\n└──────────────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────────────┐\n│ LAYER 2: Backend Gateway (Cloudflare Workers)                                │\n├──────────────────────────────────────────────────────────────────────────────┤\n│\n├─ Onboarding Service (TASK 1 - BUILT)\n│  └─ services/gateway/src/onboarding.ts\n│     ├─ getOrCreateUser(email, name, tenantId)\n│     │  └─ Query/insert D1 users table\n│     │\n│     ├─ getAvailableRoles()\n│     │  └─ Return 10 role options + suggested connectors\n│     │\n│     ├─ getAvailableConnectors(tier)\n│     │  └─ Return 15 connectors filtered by tier\n│     │\n│     └─ completeOnboarding(userId, role, connectors)\n│        ├─ Update D1 users table\n│        ├─ Store in tenant_spine_config\n│        └─ Log to audit_log\n│\n├─ Auth Endpoints (EXISTING)\n│  └─ services/gateway/src/auth.ts\n│     ├─ OAuth callback handlers (Google, GitHub)\n│     ├─ Call getOrCreateUser() (TASK 1)\n│     ├─ Issue MCP token (TODO - WIRE TASK 1)\n│     └─ Return jwt_token for session\n│\n└──────────────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────────────┐\n│ LAYER 3: MCP Server (Cloudflare Workers)                                     │\n├──────────────────────────────────────────────────────────────────────────────┤\n│\n├─ Auth Middleware (TASK 2 - BUILT)\n│  └─ services/mcp-connector/src/middleware/auth.ts\n│     ├─ validateMcpToken(authHeader, secret)\n│     │  ├─ Parse \"Bearer {token}\"\n│     │  ├─ Verify HMAC-SHA256 signature\n│     │  ├─ Check expiration (15 min)\n│     │  └─ Return: { valid, payload }\n│     │\n│     ├─ extractTenantId(headers, tokenPayload)\n│     │  └─ Get from header or token\n│     │\n│     ├─ extractUserId(headers, tokenPayload)\n│     │  └─ Get from header or token\n│     │\n│     ├─ validateTenantIsolation(requestTenantId, tokenTenantId)\n│     │  └─ Hard wall: IDs must match\n│     │\n│     └─ authMiddleware(request, secret)\n│        └─ Full pipeline: parse → verify → extract → validate\n│\n├─ Actor Resolution (TASK 2 - BUILT)\n│  └─ services/mcp-connector/src/middleware/actor.ts\n│     ├─ resolveActor(db, tenantId, userId)\n│     │  ├─ Query D1: users (email, name, role)\n│     │  ├─ Query D1: tenant_spine_config (tier, connectors)\n│     │  ├─ Derive permissions\n│     │  └─ Return: full actor context\n│     │\n│     ├─ hasPermission(actor, requiredPerm)\n│     │  ├─ Exact match\n│     │  ├─ Wildcard match (\"spine:*\")\n│     │  └─ Global wildcard (\"*\")\n│     │\n│     ├─ canAccessEntityType(db, actor, entityType)\n│     │  └─ Check: tenant configured + actor can access\n│     │\n│     ├─ hasConnectorConnected(actor, connectorId)\n│     │  └─ Check: in actor.connected_sources\n│     │\n│     └─ validateActorCanAccessTool(actor, toolName, db, context)\n│        └─ Combined permission + entity + connector gate\n│\n├─ MCP Server (EXISTING + TODO integration)\n│  └─ services/mcp-connector/src/spine-mcp-server.ts\n│     ├─ spine.query → query vault\n│     ├─ spine.get → get single entity\n│     ├─ spine.summary → stats\n│     ├─ memory.search → semantic search\n│     ├─ memory.propose → submit to governance\n│     └─ spine.connected_sources → list connectors\n│     (TODO: wire auth middleware + actor resolver before dispatch)\n│\n└──────────────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────────────┐\n│ LAYER 4: Connectors (Node.js / Web Services)                                 │\n├──────────────────────────────────────────────────────────────────────────────┤\n│\n├─ Freshsales Connector (TASK 3 - BUILT)\n│  └─ packages/connectors/src/adapters/freshsales.ts\n│     ├─ FreshsalesConnector(config)\n│     │  ├─ constructor(apiKey, accountUrl, tenantId, spineUrl)\n│     │  │  └─ Create axios client + SpineEmitter\n│     │  │\n│     │  ├─ syncContacts()\n│     │  │  ├─ GET /crm/sales/contacts (paginated 100/page)\n│     │  │  ├─ Map: Freshsales → Spine Contact\n│     │  │  ├─ Merge fields: name, email, phone, owner, engagement_level\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  ├─ syncAccounts()\n│     │  │  ├─ GET /crm/sales/accounts (paginated)\n│     │  │  ├─ Map: Freshsales → Spine Account\n│     │  │  ├─ Merge fields: name, industry, revenue, employee_count, health_score\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  ├─ syncDeals()\n│     │  │  ├─ GET /crm/sales/deals (paginated)\n│     │  │  ├─ Map: Freshsales → Spine Opportunity\n│     │  │  ├─ Merge fields: name, stage, value, probability, owner, age_days\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  └─ runFullSync()\n│     │     ├─ Promise.all([syncContacts, syncAccounts, syncDeals])\n│     │     ├─ Parallel execution\n│     │     └─ Return: count of entities synced\n│     │\n│     └─ createFreshsalesConnector(apiKey, accountUrl, tenantId, spineUrl)\n│        └─ Factory function\n│\n├─ Razorpay Connector (TASK 3 - BUILT)\n│  └─ packages/connectors/src/adapters/razorpay.ts\n│     ├─ RazorpayConnector(config)\n│     │  ├─ constructor(keyId, keySecret, tenantId, spineUrl)\n│     │  │  └─ Create axios client (basic auth) + SpineEmitter\n│     │  │\n│     │  ├─ syncCustomers()\n│     │  │  ├─ GET /customers (paginated skip/count)\n│     │  │  ├─ Map: Razorpay customer → Spine Account\n│     │  │  ├─ Merge fields: name, email, contact, gstin\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  ├─ syncInvoices()\n│     │  │  ├─ GET /invoices (paginated)\n│     │  │  ├─ Map: Razorpay invoice → Spine Invoice\n│     │  │  ├─ Merge fields: amount, currency, status, payment_status, days_overdue\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  ├─ syncSubscriptions()\n│     │  │  ├─ GET /subscriptions (paginated)\n│     │  │  ├─ Map: Razorpay subscription → Spine Subscription\n│     │  │  ├─ Merge fields: plan_id, status, start_date, mrr, renewal_date\n│     │  │  └─ Emit each via SpineEmitter\n│     │  │\n│     │  └─ runFullSync()\n│     │     ├─ Promise.all([syncCustomers, syncInvoices, syncSubscriptions])\n│     │     ├─ Parallel execution\n│     │     └─ Return: count of entities synced\n│     │\n│     └─ createRazorpayConnector(keyId, keySecret, tenantId, spineUrl)\n│        └─ Factory function\n│\n└──────────────────────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────────────────────┐\n│ LAYER 5: Data (D1 + Vectorize + Vault)                                       │\n├──────────────────────────────────────────────────────────────────────────────┤\n│\n├─ D1 Database Tables (TODO: CREATE)\n│  ├─ users\n│  │  ├─ id (PRIMARY KEY)\n│  │  ├─ email\n│  │  ├─ name\n│  │  ├─ tenant_id\n│  │  ├─ role (from onboarding)\n│  │  ├─ onboarding_completed_at (NULL if not completed)\n│  │  ├─ created_at\n│  │  └─ updated_at\n│  │\n│  └─ tenant_spine_config\n│     ├─ tenant_id (PRIMARY KEY)\n│     ├─ allowed_entity_types (JSON array)\n│     ├─ connected_connectors (JSON array, from onboarding)\n│     ├─ subscription_tier\n│     └─ updated_at\n│\n├─ Spine Vault (Encrypted)\n│  ├─ Contains: entities (contacts, accounts, invoices, etc.)\n│  ├─ Storage: D1 table (encrypted AES-256)\n│  ├─ Indexed: Vectorize (semantic search)\n│  ├─ Populated by: SpineEmitter.emit() from connectors\n│  └─ Queried by: MCP tools (spine.query, spine.get, etc.)\n│\n└──────────────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## Token Flow Diagram\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ Token Lifecycle (15 minutes)                                                │\n└─────────────────────────────────────────────────────────────────────────────┘\n\nT=0 seconds\n├─ User completes onboarding\n├─ POST /api/v1/onboarding/complete { role, connectors }\n│  ├─ Store role in D1 users table\n│  ├─ Store connectors in tenant_spine_config\n│  └─ Call: issueMcpToken(secret, tenantId, userId)\n│\n├─ Token Generation\n│  ├─ Payload: { tenant_id, user_id, exp, iat }\n│  ├─ exp = now + 15*60*1000 (15 minutes)\n│  ├─ iat = now\n│  ├─ payloadB64 = base64(JSON.stringify(payload))\n│  ├─ sig = HMAC-SHA256(payloadB64, secret)\n│  ├─ sigB64 = base64(sig)\n│  └─ token = `${payloadB64}.${sigB64}`\n│\n├─ Token in Session\n│  └─ sessionStorage.setItem('mcp_token', token)\n│\n├─ Token Used (anytime T < 15 min)\n│  └─ fetch('https://mcp.integratewise.ai/call', {\n│     headers: { Authorization: `Bearer ${token}` }\n│     })\n│\n├─ Token Validation (in MCP server)\n│  ├─ Parse: authHeader.split(' ') → ['Bearer', token]\n│  ├─ Split: token.split('.') → [payloadB64, sigB64]\n│  ├─ Decode: payload = JSON.parse(atob(payloadB64))\n│  ├─ Check: payload.exp > Date.now()\n│  ├─ Verify: sig = HMAC-SHA256(payloadB64, secret)\n│  ├─ Compare: sigB64 === btoa(sig)\n│  └─ Result: { success: true, payload }\n│\n└─ T > 15 min: Token Expired\n   └─ Validate fails: return 401 Unauthorized\n```\n\n---\n\n## Permission Gate Diagram\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ Permission Gating (Multi-Layer)                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n\nChatGPT calls: spine.query(entity_type=\"account\", filters={status: \"active\"})\n│\n├─ Layer 1: Token Validation\n│  ├─ Parse + verify HMAC\n│  ├─ Check expiration\n│  └─ Extract: tenant_id, user_id\n│     └─ Result: actor_basic = { tenant_id, user_id, _authenticated }\n│\n├─ Layer 2: Actor Resolution\n│  ├─ Query D1 users → email, name, role\n│  ├─ Query D1 tenant_spine_config → tier, connectors\n│  ├─ Derive permissions: role + tier → permission list\n│  └─ Result: actor_full = {\n│        tenant_id, user_id, email, name, role, tier,\n│        connected_sources, permissions, _authenticated\n│     }\n│\n├─ Layer 3: Tenant Isolation\n│  ├─ Check: request.tenant_id != actor.tenant_id?\n│  └─ Hard wall: 403 Forbidden if mismatch\n│\n├─ Layer 4: Permission Gate\n│  ├─ Check: actor.permissions includes \"spine:query\"?\n│  │  ├─ Exact match: \"spine:query\"\n│  │  ├─ Wildcard match: \"spine:*\"\n│  │  └─ Global: \"*\"\n│  ├─ Result: allowed or \"Permission denied\"\n│     └─ Free tier might block memory.search\n│\n├─ Layer 5: Entity Type Gate\n│  ├─ Query D1: tenant_spine_config.allowed_entity_types\n│  ├─ Check: \"account\" in allowed_entity_types?\n│  └─ Result: allowed or \"Entity type not configured\"\n│\n├─ Layer 6: Connector Gate\n│  ├─ If querying \"account\" from Razorpay source:\n│  ├─ Check: \"razorpay\" in actor.connected_sources?\n│  └─ Result: allowed or \"Connector not connected\"\n│\n└─ Layer 7: Tool Execution\n   ├─ All gates passed\n   ├─ Query vault for accounts with status=\"active\"\n   ├─ Return canonical entities\n   └─ ChatGPT generates natural language response\n```\n\n---\n\n## Key Files Reference\n\n### Documentation (Read First)\n- `PIPELINE_AUTH_TO_MEMORY.md` — 666 lines, complete spec\n- `ONBOARDING_IMPLEMENTATION.md` — 206 lines, task 1\n- `IW_BRIDGE_IMPLEMENTATION.md` — 387 lines, task 2\n- `CHATGPT_CONNECTOR_GUIDE.md` — 342 lines, task 2\n- `SPINE_HYDRATION_IMPLEMENTATION.md` — 431 lines, task 3\n- `IMPLEMENTATION_COMPLETE_TASKS_1_3.md` — 503 lines, this summary\n- `v0_memories/user/integratewise-pipeline-implementation.md` — 236 lines, quick ref\n\n### Task 1: Onboarding (9 files)\n- `services/gateway/src/onboarding.ts` — 432 lines\n- `apps/web/app/api/v1/onboarding/{start,complete}/route.ts` — ~100 lines\n- `apps/web/components/onboarding/{wizard,role-selector,connector-selector}.tsx` — ~420 lines\n- `apps/web/app/onboarding/page.tsx` — ~30 lines\n\n### Task 2: MCP Bridge (4 files)\n- `services/mcp-connector/src/middleware/auth.ts` — 209 lines\n- `services/mcp-connector/src/middleware/actor.ts` — 327 lines\n- `CHATGPT_CONNECTOR_GUIDE.md` — 342 lines\n- `IW_BRIDGE_IMPLEMENTATION.md` — 387 lines\n\n### Task 3: Connectors (2 files)\n- `packages/connectors/src/adapters/freshsales.ts` — 376 lines\n- `packages/connectors/src/adapters/razorpay.ts` — 396 lines\n\n---\n\n## What's Done vs TODO\n\n### ✅ DONE (Tasks 1-3)\n- [x] Onboarding UI + API endpoints\n- [x] 10 role options with suggested connectors\n- [x] Role → domain mapping\n- [x] MCP token generation (format, HMAC)\n- [x] Auth token validation middleware\n- [x] Actor resolution from D1\n- [x] Permission derivation (role + tier)\n- [x] Freshsales CRM adapter (contacts, accounts, deals)\n- [x] Razorpay payments adapter (customers, invoices, subscriptions)\n- [x] Merge field mapping for all entity types\n- [x] Calculated fields (engagement, health, MRR, days_overdue)\n- [x] Complete documentation\n\n### ❌ TODO (Next Immediate Steps - Blocking)\n1. **D1 Schema Creation** (prerequisite for all)\n   - Create users table\n   - Create tenant_spine_config table\n   - Create audit_log table\n\n2. **Wire Task 1: Onboarding → Auth** (enables onboarding flow)\n   - In gateway/auth.ts, call getOrCreateUser() after OAuth\n   - Redirect to /onboarding if not onboarded\n   - Issue MCP token on completion\n\n3. **Wire Task 2: Auth → MCP** (enables ChatGPT)\n   - In spine-mcp-server.ts, call authMiddleware() in dispatchSpineTool()\n   - Call resolveActor() to populate context\n   - Add permission checks before tool execution\n\n4. **Create Schema Endpoint** (for ChatGPT discovery)\n   - GET /schema.json returns tool definitions\n\n5. **Create Sync UI** (enables data population)\n   - Settings → Connectors panel\n   - Show connected connectors, last sync time\n   - \"Sync Now\" button\n\n### ⏳ TODO (Tasks 4-6)\n6. **Workspace Loader** — hydrate dashboards with Spine data\n7. **Intelligent Overlay** — risk badges, recommendations\n8. **Memory Governance** — capture, triage, promote conversations\n\n---\n\n## Success Criteria\n\n### Task 1 (Onboarding) ✅ Built → ⏳ Testing\n- [ ] New user → /onboarding\n- [ ] 10 roles render + select\n- [ ] Suggested connectors show\n- [ ] Multi-select works\n- [ ] Complete API persists to D1\n- [ ] Redirect to role workspace\n- [ ] MCP token in session\n\n### Task 2 (MCP Bridge) ✅ Built → ⏳ Integration\n- [ ] Token valid for 15 min\n- [ ] Invalid signature rejected\n- [ ] Tenant isolation enforced\n- [ ] Actor resolved from D1\n- [ ] Permissions correct per role+tier\n- [ ] Free tier blocks memory\n- [ ] Admin bypasses all checks\n- [ ] ChatGPT can authenticate\n\n### Task 3 (Connectors) ✅ Built → ⏳ Testing\n- [ ] Freshsales API credentials work\n- [ ] syncContacts() returns entities\n- [ ] Merge fields populated\n- [ ] Calculated fields correct\n- [ ] Razorpay credentials work\n- [ ] syncInvoices() returns entities\n- [ ] Parallel sync works\n- [ ] Spine receives events\n- [ ] Entities queryable via MCP\n\n---\n\n## Timeline to Full Launch\n\n**Week 1: Wiring (Tasks 1-3 integration)**\n- D1 schema creation (2 hours)\n- Auth → onboarding wiring (4 hours)\n- MCP auth integration (4 hours)\n- Testing onboarding flow (4 hours)\n- **Deliverable:** New users can complete onboarding\n\n**Week 2: Data Hydration**\n- Sync UI creation (4 hours)\n- Test Freshsales connector (4 hours)\n- Test Razorpay connector (4 hours)\n- Verify Spine data (2 hours)\n- **Deliverable:** Live data in Spine\n\n**Week 3-4: Dashboard + AI (Tasks 4-5)**\n- Workspace Loader (8 hours)\n- Normalizer (8 hours)\n- AI Home + overlays (16 hours)\n- Testing (8 hours)\n- **Deliverable:** Dashboards with live metrics\n\n**Week 5-6: Memory (Task 6)**\n- Memory capture (8 hours)\n- Triage Bot (8 hours)\n- Org memory UI (8 hours)\n- Testing (8 hours)\n- **Deliverable:** Full pipeline live\n\n**Total: 6 weeks to full activation**\n\n---\n\n## Summary\n\n**Phase Complete: 50% (Tasks 1-3 of 6)**\n\n- ✅ Auth + Onboarding fully implemented (9 files)\n- ✅ MCP Bridge with auth middleware (4 files)\n- ✅ Live connectors for Customer-Zero (2 files)\n- ✅ Complete architecture documented (2300+ lines)\n- ❌ Still needs: D1 schema + wiring + Tasks 4-6\n\n**Ready for:** Next developer to integrate + test\n\n**Blocking:** D1 schema (everything depends on it)\n\n**Next Action:** Create D1 tables + wire auth flow\n"}