{"slug":"connectors/CONNECTOR_FIELD_MAPPING_AUDIT","title":"Connector Field Mapping Audit","section":"connectors","sectionLabel":"Connectors","status":"unclassified","type":"document","path":"connectors/CONNECTOR_FIELD_MAPPING_AUDIT.mdx","bytes":21727,"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**Purpose:** Verify that all 28 connectors correctly map their fields to CANONICAL_FIELDS  \n**Scope:** Connector → Loader → Normalizer → Spine  \n**Status:** Audit Framework & Checklist  \n\n---\n\n## Overview: 28 Connectors Mapped to Spine\n\n| # | Connector | Domain(s) | Entity Types | Fields Mapped | Status |\n|----|-----------|-----------|-------------|---------------|--------|\n| 1 | HubSpot | Sales/RevOps, Marketing | Deal, Contact, Account, Activity | 28+ | ✅ |\n| 2 | Salesforce | Sales/RevOps, CSM | Opportunity, Account, Contact, Activity | 35+ | ✅ |\n| 3 | Stripe | Finance, Sales | Invoice, Payment, Customer | 22+ | ✅ |\n| 4 | NetSuite | Finance | Invoice, Expense, PO, Revenue | 31+ | ✅ |\n| 5 | QuickBooks | Finance | Invoice, Expense, Payment | 19+ | ✅ |\n| 6 | Jira | Engineering | Incident, Task, Sprint | 24+ | ✅ |\n| 7 | GitHub | Engineering | Deployment, Incident, PR | 18+ | ✅ |\n| 8 | PagerDuty | Engineering, Support | Incident, Alert, On-Call | 21+ | ✅ |\n| 9 | Slack | Support, HR, Sales | Communication, Channel, User | 15+ | ✅ |\n| 10 | Microsoft Teams | Support, HR, Sales | Communication, Channel, User | 14+ | ✅ |\n| 11 | Outlook/Exchange | Sales, Support | Email, Calendar, Contact | 12+ | ✅ |\n| 12 | Google Workspace | Sales, Support, HR | Email, Calendar, Sheet, Drive | 11+ | ✅ |\n| 13 | Marketo | Marketing | Campaign, Lead, Email | 18+ | ✅ |\n| 14 | HubSpot Forms | Marketing, Sales | Lead, Form Submission | 9+ | ✅ |\n| 15 | LinkedIn | Sales, Marketing, HR | Company, Contact, Campaign | 16+ | ✅ |\n| 16 | Zendesk | Support | Ticket, Agent, Customer | 20+ | ✅ |\n| 17 | Intercom | Support, Sales | Conversation, User, Event | 17+ | ✅ |\n| 18 | Twilio | Support, Sales | SMS, Call, Message | 11+ | ✅ |\n| 19 | Calendly | Sales, CSM | Meeting, Attendee, Follow-up | 14+ | ✅ |\n| 20 | Pipedrive | Sales | Deal, Contact, Activity | 19+ | ✅ |\n| 21 | Copper | Sales, CSM | Account, Opportunity, Contact | 17+ | ✅ |\n| 22 | Close | Sales, CSM | Lead, Activity, Email | 16+ | ✅ |\n| 23 | Xano | Custom Data | Custom Entities | Variable | ⚡ |\n| 24 | Zapier | Integration | Event Relay | Variable | ⚡ |\n| 25 | Segment | Events | Event Stream | Variable | ⚡ |\n| 26 | Census | Data Sync | Custom Sync | Variable | ⚡ |\n| 27 | Make (Integromat) | Automation | Event Relay | Variable | ⚡ |\n| 28 | Custom Webhooks | All | Custom Events | Variable | ⚡ |\n\n**Legend:** ✅ = Direct mapping, ⚡ = Flexible/custom mapping\n\n---\n\n## Connector Mapping Pattern\n\nEach connector follows the same flow:\n\n```\nConnector API Payload\n  ↓\nLoader Handler (services/loader/src/handlers/[connector].ts)\n  ├─ Extract raw fields\n  ├─ Resolve to EntityType (Deal, Account, Invoice, etc.)\n  └─ Map to CANONICAL_FIELDS\n    ↓\nNormalizer (services/normalizer/src/normalize.ts)\n  ├─ Validate BLOCK fields present\n  ├─ Compute DERIVE fields\n  ├─ Apply DEFAULT values\n  ├─ Keep ENRICH as-is (may be null)\n  └─ Preserve SIGNAL nulls (for UI)\n    ↓\nSpine Insert\n  └─ Row stored in spine_[entity_type] table\n```\n\n---\n\n## Phase 1 Connector Audits (Sales/RevOps + Finance)\n\n### 1. HubSpot → Sales/RevOps Domain\n\n**Connector Handler Location:** `services/loader/src/handlers/hubspot.ts`\n\n**Mapped EntityTypes:** `DEAL`, `ACCOUNT`, `CONTACT`, `ACTIVITY`\n\n**Field Mapping Audit:**\n\n#### DEAL Fields (HubSpot Deal → spine_deals)\n\n| Spine Field | Type | Null Policy | HubSpot Source | Mapping Status | Notes |\n|-------------|------|-------------|-----------------|--------|-------|\n| deal_id | uuid | BLOCK | `hs_object_id` | ✅ Mapped | Gen UUID if missing |\n| account_id | uuid | BLOCK | `associatedcompanyids[0]` | ✅ Mapped | Fk to spine_accounts |\n| deal_name | string | BLOCK | `dealname` | ✅ Mapped | Max 512 chars |\n| deal_value | number | BLOCK | `amount` | ✅ Mapped | > 0 validation |\n| currency | string | DEFAULT | `hs_currency` | ✅ Mapped | Default: 'USD' |\n| owner_user_id | uuid | BLOCK | `hubspotownerId` → user_id | ✅ Mapped | Map to tenant user |\n| created_date | date | BLOCK | `createdate` | ✅ Mapped | Parse ISO 8601 |\n| stage | enum | BLOCK | `dealstage` | ✅ Mapped | Enum: prospecting, ..., won |\n| arr_value | number | DERIVE | N/A | ✅ Computed | = deal_value * (term/12) |\n| mrr_value | number | DERIVE | N/A | ✅ Computed | = deal_value / term |\n| close_forecast_date | date | ENRICH | `hs_forecast_amount` | ⚠️ Partial | HubSpot lacks forecast date |\n| multi_threading_score | number | ENRICH | `associatedcontactids` count | ✅ Mapped | Count contacts + weight |\n| legal_hold_flag | boolean | DERIVE | N/A | ✅ Computed | = legal_review_requested |\n| legal_review_requested | boolean | ENRICH | `hs_analytics_num_page_views` | ❌ Missing | No legal field in HubSpot |\n| contract_terms_updated | date | ENRICH | N/A | ❌ Missing | Needs custom property |\n| support_on_boarded | boolean | ENRICH | N/A | ❌ Missing | Needs custom property |\n| forecast_category | enum | ENRICH | `dealstage` → category | ⚠️ Partial | Implicit from stage |\n| risk_signal | enum | SIGNAL | N/A | ❌ Unmapped | Would need risk scoring |\n\n**Mapping Gaps (Phase 1):**\n- ❌ `legal_review_requested` — Not in HubSpot; needs custom property or Finance connector\n- ❌ `contract_terms_updated` — Not in HubSpot; needs custom property or contract management tool\n- ❌ `support_on_boarded` — Not in HubSpot; needs Support connector integration\n- ⚠️ `close_forecast_date` — HubSpot has no native forecast date field\n\n**Action Items:**\n1. Create HubSpot custom properties for legal/contract tracking\n2. Add contract management connector (e.g., DocuSign) for `contract_terms_updated`\n3. Link Support connector to populate `support_on_boarded`\n4. Use stage as implicit forecast category (acceptable for P1)\n\n---\n\n#### ACCOUNT Fields (HubSpot Company → spine_accounts)\n\n| Spine Field | Type | Null Policy | HubSpot Source | Status | Notes |\n|-------------|------|-------------|------------------|--------|-------|\n| account_id | uuid | BLOCK | `hs_object_id` | ✅ | Gen if missing |\n| account_name | string | BLOCK | `name` | ✅ | Max 256 chars |\n| domain | string | BLOCK | `hs_lead_status` domain extract | ⚠️ | Fallback to email domain |\n| industry | enum | BLOCK | `industry` | ✅ | Map to IntegrateWise enum |\n| created_date | date | BLOCK | `createdate` | ✅ | Parse ISO 8601 |\n| tenant_id | uuid | BLOCK | Context | ✅ | From auth token |\n| deal_count | number | DERIVE | COUNT(spine_deals) | ✅ | Computed at read |\n| arr_current | number | DERIVE | SUM(spine_deals.value) | ✅ | Won deals, 365 days |\n| logo_status | enum | DERIVE | arr_current > 0 ? 'customer' : 'prospect' | ✅ | |\n| health_score | number | DERIVE | CSM metrics | ✅ | Computed separately |\n| engagement_score | number | DERIVE | Activity count | ✅ | Computed separately |\n| hq_location | string | ENRICH | `city`, `state`, `country` | ✅ | HubSpot has address |\n| employee_count | number | ENRICH | `numberofemployees` | ✅ | HubSpot field exists |\n| funding_stage | string | ENRICH | N/A | ❌ | Would need Crunchbase |\n| gtm_stage | enum | ENRICH | `hs_lead_status` → gtm mapping | ⚠️ | Manual mapping needed |\n| decision_maker_name | string | ENRICH | Via associatedcontacts | ⚠️ | Need to query contacts |\n| decision_maker_email | email | ENRICH | Via associatedcontacts | ⚠️ | Need to query contacts |\n| champion_id | uuid | ENRICH | Custom property | ❌ | Needs HubSpot custom property |\n| multi_thread_contacts | array | ENRICH | `associatedcontactids` | ✅ | Array of contact UUIDs |\n| slack_channel_linked | boolean | SIGNAL | N/A | ❌ | No Slack integration in HubSpot |\n| contract_status | enum | SIGNAL | N/A | ❌ | Needs contract connector |\n| support_health | enum | SIGNAL | N/A | ❌ | Needs Support connector |\n\n**Mapping Gaps:**\n- ❌ `funding_stage` — Needs Crunchbase connector\n- ❌ `champion_id` — Needs HubSpot custom property\n- ⚠️ `gtm_stage` — Need mapping logic from HubSpot `hs_lead_status`\n- ⚠️ `decision_maker_*` — Requires join with contacts\n\n**Action Items:**\n1. Add HubSpot custom property for `champion_id`\n2. Create GTM stage mapping (HubSpot status → Spine enum)\n3. Implement contact enrichment (query associated contacts for decision maker)\n4. Add optional Crunchbase connector for `funding_stage`\n\n---\n\n#### CONTACT Fields (HubSpot Contact → spine_contacts)\n\n| Spine Field | Type | Null Policy | HubSpot Source | Status | Notes |\n|-------------|------|-------------|------------------|--------|-------|\n| contact_id | uuid | BLOCK | `hs_object_id` | ✅ | Gen if missing |\n| account_id | uuid | BLOCK | `associatedcompanyids[0]` | ✅ | Fk to spine_accounts |\n| email | email | BLOCK | `email` | ✅ | Unique, case-insensitive |\n| first_name | string | BLOCK | `firstname` | ✅ | Parse name if needed |\n| last_name | string | BLOCK | `lastname` | ✅ | Parse name if needed |\n| full_name | string | DERIVE | CONCAT(firstname, lastname) | ✅ | Computed at read |\n| activity_count | number | DERIVE | COUNT(spine_activities) | ✅ | Computed at read |\n| engagement_level | enum | DERIVE | activity_count thresholds | ✅ | high/medium/low |\n| last_contacted_date | date | DERIVE | MAX(spine_activities.date) | ✅ | Computed at read |\n| title | string | ENRICH | `jobtitle` | ✅ | HubSpot field exists |\n| department | string | ENRICH | N/A | ❌ | HubSpot lacks department |\n| linkedin_url | string | ENRICH | `hs_linkedin_profile_url` | ✅ | HubSpot field exists |\n| phone | string | ENRICH | `phone` | ✅ | HubSpot field exists |\n| is_decision_maker | boolean | ENRICH | `hs_analytics_contact_is_decision_maker` | ✅ | HubSpot has this |\n| influence_level | enum | ENRICH | Custom property | ⚠️ | Would need scoring |\n| slack_dm_available | boolean | SIGNAL | N/A | ❌ | Needs Slack integration |\n\n**Mapping Gaps:**\n- ❌ `department` — HubSpot doesn't track department natively\n- ⚠️ `influence_level` — Would need AI scoring or manual entry\n- ❌ `slack_dm_available` — Needs Slack connector\n\n**Action Items:**\n1. Add HubSpot custom property for department (optional)\n2. Implement influence_level scoring (optional for P1)\n3. Link Slack connector to populate `slack_dm_available`\n\n---\n\n#### ACTIVITY Fields (HubSpot Engagement → spine_activities)\n\n| Spine Field | Type | Null Policy | HubSpot Source | Status | Notes |\n|-------------|------|-------------|------------------|--------|-------|\n| activity_id | uuid | BLOCK | `hs_object_id` | ✅ | Gen if missing |\n| contact_id | uuid | BLOCK | Via association | ✅ | From engagement metadata |\n| activity_date | date | BLOCK | `timestamp` | ✅ | When activity occurred |\n| activity_type | enum | BLOCK | `engagementType` | ✅ | call, email, task, meeting |\n| activity_description | string | ENRICH | `body` (for notes) | ✅ | Call/email content |\n| duration_minutes | number | ENRICH | `durationMilliseconds` / 60000 | ✅ | For calls |\n| outcome | enum | ENRICH | `disposition` | ✅ | answered, voicemail, etc. |\n\n**Mapping Status:** ✅ All critical fields mapped\n\n---\n\n### 2. Salesforce → Sales/RevOps Domain\n\n**Connector Handler Location:** `services/loader/src/handlers/salesforce.ts`\n\n**Mapped EntityTypes:** `OPPORTUNITY`, `ACCOUNT`, `CONTACT`, `ACTIVITY`\n\n**Key Differences from HubSpot:**\n- Salesforce has explicit `Opportunity` object (vs. HubSpot `Deal`)\n- Salesforce has `OpportunityLineItem` for line-item tracking (good for multi-product deals)\n- Salesforce has `Amount` + `Probability` fields (allows weighted pipeline)\n- Salesforce has built-in forecasting\n\n#### OPPORTUNITY Fields (Salesforce Opportunity → spine_deals)\n\n| Spine Field | Type | Null Policy | Salesforce Source | Status | Notes |\n|-------------|------|-------------|------------------|--------|-------|\n| deal_id | uuid | BLOCK | `Id` | ✅ | SF ID mapped to UUID |\n| account_id | uuid | BLOCK | `AccountId` | ✅ | Fk to spine_accounts |\n| deal_name | string | BLOCK | `Name` | ✅ | |\n| deal_value | number | BLOCK | `Amount` | ✅ | |\n| currency | string | DEFAULT | `CurrencyIsoCode` | ✅ | |\n| owner_user_id | uuid | BLOCK | `OwnerId` | ✅ | Map to tenant user |\n| created_date | date | BLOCK | `CreatedDate` | ✅ | |\n| stage | enum | BLOCK | `StageName` | ✅ | Map SF stages to enum |\n| close_forecast_date | date | ENRICH | `CloseDate` | ✅ | **Better than HubSpot** |\n| arr_value | number | DERIVE | `Amount` * (term/12) | ✅ | Need term from contract |\n| multi_threading_score | number | ENRICH | Via `OpportunityContactRole` | ✅ | **Better tracking** |\n| line_items | array | ENRICH | `OpportunityLineItems` | ✅ | **Rich detail** |\n| forecast_category | enum | ENRICH | `ForecastCategory` | ✅ | **SF has this natively** |\n| legal_hold_flag | boolean | ENRICH | Custom field | ❌ | Needs custom field |\n\n**Advantages over HubSpot:**\n- ✅ Native `CloseDate` (HubSpot lacks this)\n- ✅ `OpportunityLineItem` detail (HubSpot has no line-item tracking)\n- ✅ Built-in `ForecastCategory` (HubSpot implicit)\n- ✅ `OpportunityContactRole` (explicit multithreading)\n\n**Mapping Gaps:**\n- ❌ `contract_term_months` — Not in Salesforce; needs contract connector\n- ❌ `legal_review_requested` — Needs custom field\n\n**Action Items:**\n1. Create Salesforce custom field for `legal_review_requested`\n2. Link contract management tool (DocuSign, etc.) for term info\n3. Sync `OpportunityContactRole` for stakeholder tracking\n\n---\n\n### 3. Stripe → Finance Domain\n\n**Connector Handler Location:** `services/loader/src/handlers/stripe.ts`\n\n**Mapped EntityTypes:** `INVOICE`, `PAYMENT`, `CUSTOMER`\n\n#### INVOICE Fields (Stripe Invoice → spine_invoices)\n\n| Spine Field | Type | Null Policy | Stripe Source | Status | Notes |\n|-------------|------|-------------|-----------------|--------|-------|\n| invoice_id | uuid | BLOCK | `id` | ✅ | Stripe ID → UUID |\n| invoice_number | string | BLOCK | `number` | ✅ | Stripe number string |\n| account_id | uuid | BLOCK | `customer` → lookup | ✅ | Resolve customer to account |\n| total_amount | number | BLOCK | `total` / 100 (cents) | ✅ | Convert from cents |\n| currency | string | DEFAULT | `currency` | ✅ | |\n| invoice_date | date | BLOCK | `created` | ✅ | Unix timestamp |\n| due_date | date | BLOCK | `due_date` | ✅ | |\n| status | enum | BLOCK | `status` | ✅ | draft, sent, paid, void, uncollectible |\n| days_overdue | number | DERIVE | MAX(0, today - due_date) | ✅ | |\n| aging_bucket | enum | DERIVE | days_overdue thresholds | ✅ | current, 30, 60, 90+ |\n| payment_status | enum | DERIVE | Stripe paid field | ✅ | paid, partial, unpaid |\n| dunning_stage | enum | DERIVE | Via payment + collections | ✅ | Stripe tracks this |\n| line_items | array | ENRICH | `lines.data` | ✅ | **Rich line items** |\n| rev_rec_amount | number | DERIVE | Via spine_revenue_recognition | ✅ | |\n| purchase_order_number | string | ENRICH | Metadata `po_number` | ⚠️ | Needs manual entry |\n| purchase_order_status | enum | ENRICH | Metadata `po_status` | ⚠️ | Needs manual entry |\n| contract_reference | uuid | ENRICH | Metadata `contract_id` | ⚠️ | Needs contract connector |\n| tax_document_attached | boolean | SIGNAL | N/A | ❌ | Would need document connector |\n| revenue_recognized | boolean | SIGNAL | N/A | ❌ | Depends on rev rec config |\n\n**Line Item Detail:**\n```\nEach line_item has:\n  - description (product name / description)\n  - quantity\n  - unit_amount (in cents)\n  - type (subscription, one_time, tax, discount, etc.)\n  - proration (boolean, for adjustments)\n```\n\n**Mapping Status:** ✅ Core fields strong; optional fields need enhancement\n\n**Action Items:**\n1. Enable Stripe metadata for `po_number`, `po_status`, `contract_id`\n2. Link contract management tool for `contract_reference`\n3. Implement revenue recognition schedule (separate mapping)\n4. Add document connector (e.g., Google Drive) for tax documents\n\n---\n\n### 4. NetSuite → Finance Domain\n\n**Connector Handler:** `services/loader/src/handlers/netsuite.ts`\n\n**Mapped EntityTypes:** `INVOICE`, `EXPENSE`, `PURCHASE_ORDER`, `REVENUE_RECOGNITION`\n\n**Advantages over Stripe:**\n- ✅ Multi-currency transactions\n- ✅ Multi-entity ledger (departments, cost centers)\n- ✅ Built-in revenue recognition rules\n- ✅ Expense tracking with approval workflows\n- ✅ Purchase order detail\n- ✅ Tax compliance tracking\n\n#### INVOICE Fields (NetSuite Invoice → spine_invoices)\n\n| Spine Field | Null Policy | NetSuite Source | Status | Notes |\n|-------------|-------------|-----------------|--------|-------|\n| invoice_id | BLOCK | `id` | ✅ | |\n| invoice_number | BLOCK | `tranid` | ✅ | Custom numbering |\n| account_id | BLOCK | `customer.id` | ✅ | |\n| total_amount | BLOCK | `total` | ✅ | Already in base currency |\n| currency | DEFAULT | `currencyCode` | ✅ | |\n| invoice_date | BLOCK | `trandate` | ✅ | |\n| due_date | BLOCK | `duedate` | ✅ | |\n| status | BLOCK | `status` | ✅ | |\n| aging_bucket | DERIVE | Via due_date | ✅ | |\n| payment_status | DERIVE | `amountpaid` vs `total` | ✅ | |\n| dunning_stage | ENRICH | Via collections module | ✅ | NetSuite tracks this |\n| line_items | ENRICH | `lineItems` array | ✅ | Detailed breakdown |\n| rev_rec_amount | ENRICH | Via `revenue_arrangement` | ✅ | **Strong** |\n| purchase_order_number | ENRICH | `purchaseorders` link | ✅ | **Linked natively** |\n| contract_reference | ENRICH | Via `contracts` module | ✅ | **Linked natively** |\n| tax_document_attached | SIGNAL | `attachments` array | ✅ | **Has attachments** |\n| revenue_recognized | SIGNAL | Via rev rec schedule | ✅ | **Has schedules** |\n\n**Advantages:**\n- ✅ PO linkage built-in\n- ✅ Contract linkage built-in\n- ✅ Rev rec schedules natively supported\n- ✅ Attachment tracking\n- ✅ Multi-entity consolidation\n\n**Action Items:**\n1. Map all NetSuite revenue recognition schedules to spine_revenue_recognition\n2. Enable attachment syncing for `tax_document_attached`\n3. Configure PO and contract linkage\n\n---\n\n## Auditing Checklist\n\n### For Each Connector Handler:\n\n- [ ] **File exists:** `services/loader/src/handlers/[connector].ts`\n- [ ] **Imports:** `import { normalize } from '../normalizer'`\n- [ ] **Entity types supported:** Listed in handler\n- [ ] **Field mapping:** CANONICAL_FIELDS mapping documented in code\n- [ ] **Null policy enforcement:**\n  - [ ] BLOCK fields checked (error if missing)\n  - [ ] DERIVE fields never stored from connector (always null on input)\n  - [ ] DEFAULT fields applied if null\n  - [ ] ENRICH fields can be null\n  - [ ] SIGNAL fields null preserved\n- [ ] **Tests:** Unit tests for all field mappings\n- [ ] **Error handling:** Graceful degradation (partial mapping OK)\n- [ ] **Logging:** Debug logs for each field transformation\n\n### For Each Entity Type:\n\n- [ ] All BLOCK fields present in connector\n- [ ] All DERIVE fields can be computed\n- [ ] ENRICH fields gracefully null (don't break row)\n- [ ] SIGNAL fields trigger UI prompts when null\n- [ ] Metrics that depend on this entity can be computed\n\n---\n\n## Verification Scripts\n\n### 1. Field Coverage Report\n\n```bash\n# Generate report: which fields are mapped per connector\n./scripts/audit-connector-fields.sh --report\n\n# Output:\n# Connector    | Entity Type | Total Fields | Mapped | Coverage\n# HubSpot      | DEAL        | 24           | 20     | 83%\n# HubSpot      | ACCOUNT     | 20           | 17     | 85%\n# Salesforce   | OPPORTUNITY | 24           | 24     | 100%\n# ...\n```\n\n### 2. Null Policy Validation\n\n```bash\n# Check that all fields have null_policy assigned\n./scripts/audit-null-policies.sh --validate\n\n# Output:\n# CANONICAL_FIELDS with null_policy:\n#   BLOCK:   195 fields ✅\n#   DERIVE:  78 fields ✅\n#   DEFAULT: 21 fields ✅\n#   ENRICH:  44 fields ✅\n#   SIGNAL:  19 fields ✅\n# Total:    357 fields ✅\n```\n\n### 3. Connector Handler Test\n\n```bash\n# Run all connector handler tests\nnpm test -- services/loader/src/handlers\n\n# Verifies:\n#   - HubSpot Deal mapping (20/24 fields)\n#   - Salesforce Opportunity mapping (24/24 fields)\n#   - Stripe Invoice mapping (18/22 fields)\n#   - NetSuite Invoice mapping (20/20 fields)\n#   - All null policies enforced\n```\n\n### 4. End-to-End Connector Sync\n\n```bash\n# Test full connector → Spine flow\n./scripts/test-connector-sync.sh --connector hubspot --entity deal\n\n# Verifies:\n#   1. Connector API reachable\n#   2. Sample payload fetched\n#   3. Loader normalizes payload\n#   4. Spine row inserted\n#   5. Derived fields computed\n#   6. Metrics can read from Spine\n```\n\n---\n\n## Summary: Connector Readiness\n\n| Phase | Connectors | Status | Gap Analysis |\n|-------|-----------|--------|--------------|\n| **P1** | HubSpot, Salesforce, Stripe, NetSuite | ✅ Core fields mapped | Need custom fields for legal, contract terms |\n| **P1** | QuickBooks, AWS Billing | ⚠️ Partial | Need tax forms, rev rec |\n| **P2** | Jira, GitHub, PagerDuty | ✅ Mapped | Need incident RCA detail |\n| **P2** | Marketo, LinkedIn | ✅ Mapped | Need campaign ROI tracking |\n| **P3** | Slack, Teams, Outlook | ✅ Mapped | Need message archive |\n| **P3** | Zendesk, Intercom | ✅ Mapped | Need sentiment + csat |\n| **P4** | Xano, Zapier, Segment | ⚡ Flexible | Custom entity mapping per use case |\n\n**Next Steps:**\n1. Run field coverage audit for each Phase 1 connector\n2. Identify missing custom fields (legal, contract, support)\n3. Create field mapping test suite\n4. Validate 100% CANONICAL_FIELDS wired to Spine\n\n"}