# CloudSense Upgrade Compass Review & Customer Compass Architecture Decisions

Reference document capturing all refactoring discussions, tool reviews, and architectural decisions made during the transition from the original Upgrade Compass to the new Customer Compass.

---

## 1. Tool-by-Tool Review of the Original Upgrade Compass

### Tool 1: `connect_lma` (243 lines)

**What it does:** Entry point. Authorizes connection to the LMA Salesforce org. Runs `sf org display`, `sf org login web`, and `sf sobject describe` on 3 LMA objects.

**Simplification opportunities:**
- SF CLI installation check -- AI can run `sf version` itself
- Next-step guidance hardcoded in output -- system prompt already handles orchestration
- Markdown report assembly -- tool should return structured data, let AI present
- Field discovery re-runs every time with no cache/skip logic
- `_find_lma_org` function is dead code (never called)

**Proposed split:** `check_lma_auth` (read-only check) + `authorize_lma` (browser login). Field discovery could be lazy.

### Tool 2: `find_customer` (168 lines)

**What it does:** Searches for customer by name using SOSL with SOQL LIKE fallback.

**SOQL/Queries:**
```sql
-- SOSL (primary)
FIND {nbnco} IN ALL FIELDS RETURNING Account(Id, Name, Type WHERE Type != 'Prospect')

-- SOQL LIKE (fallback)
SELECT Id, Name, Type FROM Account WHERE Name LIKE '%nbnco%'
AND (Type != 'Prospect' OR Type = null) ORDER BY Name LIMIT 20
```

**Issues found:**
- SQL injection: basic `replace("'", "\\'")` escape -- not robust
- No pagination indication when results truncated
- `Type != 'Prospect'` is hardcoded business rule
- Single-match auto-save to state without user confirmation
- SOSL found to be unreliable -- sometimes returns no results or incorrect results

**Decision for Customer Compass:** Swap to SOQL LIKE as primary, SOSL as fallback. Add two-phase confirm flow.

### Tool 3: `get_customer_licenses` (751 lines)

**What it does:** The heaviest data-gathering tool. Runs 5+ SOQL queries for licenses, namespace resolution, available versions, health scoring.

**Queries:**
- Q1: Production licenses by Account ID
- Q2: Sandbox licenses by `Company__c LIKE '%keyword%'`
- Q3: Namespace resolution -- **N+1 problem** (separate SOQL per package)
- Q4: Available versions (bulk query)
- Q5: Installed version names

**Health Score Algorithm (0-100):**

| Dimension | Max Points | Issue |
|-----------|-----------|-------|
| Versions behind latest | 30 | Functional |
| License expiry | 30 | Functional |
| Unused licenses | 20 | Functional |
| Sandbox version drift | 10 | Always hardcoded to 10 (placeholder) |
| Package coverage | 10 | Always hardcoded to 10 (placeholder) |

Two placeholder dimensions inflate every score by 20 points.

**Should be broken into:**
- `get_customer_licenses` (simplified) -- just 2 license SOQL queries
- `get_package_versions` -- query available versions + resolve namespaces
- Health scoring left to AI

**Decision for Customer Compass:** Simplified to exactly 2 SOQL queries (~10s total). No namespace resolution, health scoring, or version lookups.

### Tool 4: `get_portfolio_insights` (593 lines)

**What it does:** Cross-customer portfolio analysis. Queries ALL active production licenses, scores each customer.

**Issues:**
- `expiry_horizon_days` parameter accepted but **never used** (dead parameter)
- No SOQL LIMIT on bulk query
- Duplicate scoring logic with Tool 3 using different formulas
- Heatmap truncation hardcoded at 50 customers

### Tool 5: `init_assessment` (393 lines)

**What it does:** Creates SFDX project, validates prerequisites (sf CLI, git, GitHub), handles org authorization.

**Should be broken into:**
- `check_prerequisites` -- validate sf CLI, git, GitHub access
- `create_assessment_project` -- create SFDX project + dirs
- `authorize_customer_org` -- validate/authorize sandbox

**Issues:**
- Double state write may overwrite data
- `warnings` list created but never populated (dead code)
- No validation of `target_version` format
- Production org detection proceeds on `Type: Unknown` instead of blocking
- Org login NOT performed by tool (unlike `connect_lma`) -- inconsistency

### Tool 6: `get_installed_packages` (340 lines)

**What it does:** Reshapes LMA data from state into `installed-packages.json`, optionally cross-references sandbox via `sf package installed list`.

**Key finding:** Borderline unnecessary -- reads data from state, adds `has_cloud_service` flags (hardcoded 3 entries), writes JSON. AI could do all of this.

**Issues:**
- Cloud service flagging is 3 hardcoded entries
- Resume check has no `force_rerun` parameter
- Packages list can be silently empty and tool still marks step completed

### Tool 7: `get_customer_metadata` (418 lines) -- HIGH TIMEOUT RISK

**What it does:** Retrieves metadata via 5 sequential batches of `sf project retrieve start`. Uses adaptive binary split for Salesforce 10,000 file limit.

**The 5 Batches:** code, automation, objects, ui_resources, security

**Timeout math:** All 5 batches run sequentially. Each takes 2-5 min. Total: 10-25 minutes. MCP times out at 60s.

**Binary split limitation:** Only splits across types, not within a type. 10,000 Apex classes in a single type = dead end.

### Tool 8: `get_managed_package_objects` (412 lines) -- HIGH TIMEOUT RISK

**What it does:** Discovers CS-namespaced sObjects, retrieves in batches of 50.

**Timeout:** 200+ CS objects = 4+ batches = 2-8 minutes. Guaranteed timeout.

**Bug found:** `_get_cs_namespaces_from_state()` called without required `working_dir` argument -- would crash.

### Tool 9: `get_customer_json_settings` (330 lines) -- MEDIUM-HIGH TIMEOUT RISK

**What it does:** Scans XML for custom settings, describes fields, exports values. 2 CLI calls per setting.

**Timeout math:** 10 settings = 20 CLI calls = 60-160s. Exceeds 60s for any org with >5 custom settings.

### Tool 10: `get_customer_object_customizations` (410 lines) -- NO TIMEOUT RISK

**What it does:** Pure local filesystem processing. Scans retrieved metadata for customer-added fields on CS objects.

**Key finding:** Cleanest tool in the codebase. No external calls, runs fast, focused on one job.

### Tool 11: `get_package_repo_mapping` (965 lines) -- MEDIUM TIMEOUT RISK

**What it does:** Most complex tool. Maps packages to GitHub repos, resolves from/to git refs.

**Issues:** Hardcoded fallback repo dictionaries, regex limitations, ~60 lines of duplicate entry-building code, `_validate_ref_exists` is dead code.

### Tool 12: `clone_package_repos` (679 lines) -- HIGH TIMEOUT RISK

**What it does:** Clones each package repo at from + to git refs. 15 packages = 30 shallow clones.

**Timeout:** 15 packages x 2 clones x 10-30s = 5-17 minutes. Guaranteed timeout.

**Good patterns:** `force_reclone` + skip-if-exists is idempotent. The `packages` parameter already exists -- AI just needs to call per-package.

---

## 2. Timeout / MCP Client Timeout Strategy

### The Core Problem

Cursor MCP client has a 60-second timeout. Every `sf` CLI subprocess takes 3-10 seconds just for startup + auth + network round-trip.

### Timeout Risk Summary

| Tool | Fix | Effort |
|------|-----|--------|
| `get_customer_metadata` | Replace with `list_metadata` + `retrieve_metadata` | 2-3 hrs |
| `get_managed_objects` | Add `namespaces` parameter | 15 min |
| `get_cs_settings` | Add `setting_names` parameter | 15 min |
| `clone_package_repos` | System prompt: call per-package (already supported) | 15 min |

### The `list_metadata` + `retrieve_metadata` Architecture

The current `get_customer_metadata` has ~300 lines of orchestration code (batching, binary split, retry). But the AI is already an orchestrator.

| Capability | Built into current tool | AI does natively |
|-----------|------------------------|------------------|
| Decide what to retrieve | Hardcoded batches | Knows from installed packages |
| Chunk large sets | Binary split (can't split within a type) | Can chunk any list by size |
| Retry on failure | Recursive binary split | Retry just the failed call |
| Report progress | After all batches complete | Between each call |
| Parallelize | Can't -- sequential loop | Can issue parallel tool calls |

**Two proposed simple tools:**
- `list_metadata` (~40 lines): `sf org list metadata --type <type>`. Returns member names + count. Runtime: 3-10 seconds.
- `retrieve_metadata` (~50 lines): Takes specific member names, generates `package.xml`, runs `sf project retrieve start`. Runtime: 10-60 seconds per chunk.

**Real-world timing from p2dev org:**

| Metadata Type | Count | Discovery Time |
|---------------|-------|-----------|
| ApexClass | 5,357 | 11s |
| CustomObject | 750 | 3s |
| ApexTrigger | 252 | 3s |
| LightningComponentBundle | 211 | 4s |
| Flow | 130 | 3s |

**Performance comparison:**

| | Current tool | Two simple tools |
|-|-------------|------------------|
| Total wall time | 15-25 min (sequential) | ~3 min (parallel) |
| MCP timeout | Guaranteed timeout | Never times out |
| Handles 10K single type | No (dead end) | Yes (AI chunks) |
| Progress reporting | None until complete | After each round |
| Failed chunk retry | Re-run everything | Retry just that chunk |
| Code complexity | ~400 lines + binary split | ~90 lines total |

### `connect_lma` Browser Login Timeout Fix (Implemented)

1. Try `sf org display` first (already authorized? done)
2. Fuzzy match from `sf org list` (authorized under different alias? done)
3. Open browser via `sf org login web` with 60s timeout
4. If timeout: return friendly message with manual recovery steps
5. Kill stale OAuth server on port 1717 to prevent zombie processes
6. After login: validate org by querying `sfLma__Package__c`

---

## 3. MVP vs Full Refactoring Decision

**Decision: Ship the MVP now. Refactor later.**

### NOT blocking MVP:
- Markdown report formatting in tools -- annoying but harmless
- Next-step guidance hardcoded in responses -- redundant but not broken
- Monolithic tools -- work fine as-is, splitting is v2
- Scoring logic in tools vs AI -- deterministic in-tool is defensible for MVP
- Dead code -- cosmetic
- Duplicate scoring logic -- inconsistent but both work independently

### Worth fixing before shipping (30 min total):
- `expiry_horizon_days` param accepted but ignored in `get_portfolio_insights`
- 2 health score dimensions always return full marks (inflates by 20pts)
- No pre-check that `connect_lma` was called in `find_customer`, `get_customer_licenses`

---

## 4. Architecture Decisions for Customer Compass

### Why Start From Scratch

User concern: "I am doing a lot of refactoring and fear to lose my working modules." Decision to create new repo `CloudSense Customer Compass`, keeping the old Upgrade Compass intact as reference.

### State Management Evolution

1. **Initially:** State stored everything -- org alias, username, ID, instance URL, customer account ID/name/slug, license count, recommended sandbox
2. **Found:** Only 3 fields actually read by other tools
3. **Problem:** Single `lma_customer_account_id` breaks with multi-customer queries
4. **Final decision:** State = LMA connection only (`lma_org_alias`). AI passes customer data explicitly as parameters.

### Global Error Handling

Single catch-all in `server.py` at the `call_tool` handler instead of per-tool try/except. Error response includes error type, detail, and diagnostic guide. All tools protected automatically.

### `licenses.json` Size Optimization

Original: 1.7MB for NBN (3,339 sandbox license records, 85% Uninstalled).

| Approach | Size | Reduction |
|----------|------|-----------|
| Current | 1,693 KB | -- |
| Strip `attributes` only | 1,185 KB | 30% |
| Group by org + lean fields + strip `attributes` | 388 KB | **77%** |

Salesforce `attributes` field (REST API metadata: `type` + `url`) is on every record and is useless. All statuses including "Uninstalled" are kept. Sandbox records grouped by org ID.

---

## 5. Tool Design Philosophy

### Core Principle: "Make the tools dumb, let the AI be smart"

### What Tools Should Do:
- Execute focused, fast operations (single SOQL query, single CLI command)
- Return structured data (not formatted markdown reports)
- Stay within 60s MCP timeout
- Write raw artifacts to files for later reference
- Accept filter parameters so AI can control scope

### What AI Should Orchestrate:
- Deciding what to retrieve and in what order
- Chunking large datasets for multiple calls
- Parallel execution of independent operations
- Progress reporting between calls
- Retrying failed individual chunks
- Presenting results contextually to users
- Computing scores and generating insights from raw data
- Disambiguation and confirmation flows with users
