# NexusCRM Project Analysis v2 — Synthesis Report

**Date**: 2026-03-31
**Method**: 3-lane structured research (R1: feasibility, R2: correctness, R3: ops/rollout) + synthesis

---

## v1 -> v2: What Changed

| v1 Claim | v2 Verdict | Why |
|----------|-----------|-----|
| P0 Security: test OAuth creds in prod (#156) | **Already fixed** | `shouldSeedTestApp()` allowlist gate merged. `resolveAdminPassword` hard-fails in prod/staging. |
| UI API childRelationships is "systemic blocker" | **Cosmetic, not blocking** | 0 runtime consumers in frontend feature code. Only in type definitions + tests. Demoted to Tier 2. |
| 32 issues from "wrong smart layout" bug | **Confirmed, but root cause refined** | Not a code bug — intentional design. `standard_layouts.json` exists but is dead code. `CleanupHardcodedLayouts` actively deletes seeded layouts at boot. |
| Boolean validation blocks saves (#248) | **Mostly fixed, residual gap** | PR #521 fixed SchemaValidator + ValidationService. Gap remains in `NormalizeSObject` (3-line fix). |
| Related list "lacks deduplication" | **Wrong — dedup EXISTS** | `metadata_layouts.go:140-146` checks `(ObjectAPIName, LookupField)` before adding. Real issue is missing system object filtering + same-object-via-different-lookups. |
| `splitCamelCase` doesn't handle PascalCase | **Wrong — it works correctly** | Regex `([a-z])([A-Z])` at `discover_tables.go:629` correctly produces "Created Date" from "CreatedDate". |
| Quote AutoNumber root cause is frontend filtering | **Partially wrong** | Real root cause is backend: `CheckFieldEditabilityWithUser` SuperUser bypass makes autoNumber `updateable=true` for admin. Frontend correctly trusts `updateable`. |
| SaveLayout preserves admin edits | **WRONG — data loss hazard** | `SaveLayout` does NOT set `IsCustomized=1`. Next `currentLayoutVersion` bump silently deletes admin-customized layouts. |

### R2 Conflict on Issue Numbers — Resolved

R2 claimed v1 issue numbers are wrong (e.g., "#156 is about SOQL walker bugs, not OAuth"). This is itself incorrect. R2 likely checked PR/commit numbers in the `pingcap-inc` repo, not issue numbers in the `ywang-official` issue tracker. The v1 agents fetched issues from the correct repo (`ywang-official/MetaDrivenPlatform4AI`) using `gh issue view`. **v1 issue numbers are correct.**

---

## The 5 Critical Findings (Ranked)

### 1. BLOCKER: Layout Data Never Loaded (32+ issues)

**What**: `standard_layouts.json` contains 9 curated Salesforce-parity layouts (Account, Contact, Opportunity, Lead, Campaign, Order, Contract, Quote, Case). It is **dead code** — no `//go:embed` or loader reads it. Additionally, `CleanupHardcodedLayouts()` in `bootstrap/layouts.go:42-53` actively DELETEs any `std_layout_*` rows from `_System_PageLayout` on every startup.

**Why it was removed**: Comment at `layouts.go:37-39` explains: the old standard layouts "contained only 8-27 standard fields, hiding 200+ custom fields." The cleanup was intentional.

**Why this matters**: Every object falls to Tier 3 auto-generation. While `generateSmartLayout` is sophisticated (5-section classifier with prefix grouping), it cannot match Salesforce's curated section names and field ordering.

**The design tension** (R1 + R2 agree): Loading the JSON as-is would recreate the original problem (hiding custom fields). But NOT loading it means no Salesforce-parity layouts ever. The solution must **merge curated structure with live schema fields**.

**Recommended fix**:
1. Load `standard_layouts.json` as section TEMPLATES (section names + key fields per section)
2. Augment each section with additional fields from live schema that match section patterns
3. Set `IsCustomized=1` on seeded layouts so version bumps don't wipe them
4. Fix `SaveLayout` to auto-set `IsCustomized=1` (prevents silent data loss of admin edits)

**Files**: `bootstrap/layouts.go`, `metadata_layouts.go`, `cmd/server/main.go:157`

**Evidence**: R1-Q1, R2-Q1, R3-Q4 all converge on this.

---

### 2. HIGH: AutoNumber/Formula Fields Writable by Admin (Quote P0 + others)

**What**: `CheckFieldEditabilityWithUser` at `permissions/service.go:152-153` has a SuperUser bypass that makes ALL non-system fields `updateable=true` for admin users — including autoNumber and formula fields. Frontend correctly trusts `updateable` from describe, so autoNumber fields appear in PATCH payload. Backend `SchemaValidator` then rejects them.

**Impact**: Blocks Quote editing for admin users (P0). Likely affects any object with autoNumber fields edited by admins.

**Recommended fix**: In `CheckFieldEditabilityWithUser`, after the SuperUser bypass, add:
```go
if field.AutoNumber || field.IsCalculated {
    return false
}
```
This is a 3-line backend fix. Frontend needs zero changes.

**Files**: `backend/internal/features/permissions/service.go:152-153`

**Evidence**: R2-Q4 (autoNumber root cause analysis)

---

### 3. HIGH: Field Label Quality (18 issues)

**What**: v1 claimed `splitCamelCase` is broken. R2 proved it works correctly. The real label issues are:

1. **416 fields in `system_tables.json` originally missing labels** — Fixed by PR #481 (added 417 labels + frontend `humanizeFieldName` fallback). But issue #355 reports labels reset after data refresh.
2. **Auto-discovered fields from DB columns** — `discover_tables.go:283-294` uses `INSERT IGNORE` so pre-seeded labels are preserved. But dynamically discovered fields (not in `system_tables.json`) get `Label = fieldNameToLabel(apiName)` which is correct for camelCase but not for custom fields with underscores.
3. **Frontend fallback exists** — `humanizeFieldName()` was added as a frontend fallback. But some code paths still display raw labels.

**Root cause is NOT one thing** — it's multiple label sources with different quality levels. The fix depends on which object/field is affected.

**Recommended fix**: Audit which specific objects still have label issues. The bulk fix (PR #481) addressed most cases. Remaining issues are likely edge cases in auto-discovered fields.

**Evidence**: R2-Q4 (splitCamelCase works), R1-Q4 (PR #481 merged)

---

### 4. MEDIUM: Boolean NormalizeSObject Gap (6 issues)

**What**: PR #521 fixed validation rejection (SchemaValidator + ValidationService both accept `float64`). But `NormalizeSObject` at `schema_utils.go:43-53` does NOT coerce `float64 -> bool`. The value passes validation but reaches TiDB as `float64(0)` instead of `true/false`.

**Impact**: May cause subtle display/comparison bugs. TiDB likely handles `float64` for `TINYINT` columns, but the read-back path also doesn't handle `float64`.

**Recommended fix**: Add `float64` case to `NormalizeSObject` boolean handler:
```go
case float64:
    record[field.APIName] = v != 0
```
Also add to read-back at `schema_utils.go:161-177`. Total: ~6 lines.

**Files**: `backend/internal/infrastructure/persistence/schema_utils.go:43-53, 161-177`

**Evidence**: R1-Q2

---

### 5. MEDIUM: Related List Filtering (Not Deduplication) (6 issues)

**What**: v1 said dedup is missing. R2 proved dedup EXISTS at `metadata_layouts.go:140-146`. The real issue is:
1. **No system object filtering** — Share, FieldHistory, Feed objects appear
2. **Same object via multiple lookups** — e.g., Account has multiple Contact lookup fields, each creates a related list entry. The dedup check uses `(ObjectAPIName, LookupField)` so different lookups from same object are not deduplicated.
3. **No sort/priority** — All related lists added in DB query order, not business relevance.

**Recommended fix**: Add system object exclusion list + sort by business priority in `augmentLayoutWithRelatedLists`.

**Files**: `backend/internal/features/metadata/metadata_layouts.go:128-167`

**Evidence**: R2-Q4 (dedup code verified), R3-Q1

---

## Deployment Plan (3 Phases)

### Phase 1: Independent Low-Risk Fixes (est. 37 issues closed)

| Fix | Files | Lines Changed | Test Coverage | Risk |
|-----|-------|---------------|---------------|------|
| AutoNumber/formula updateable bypass | `permissions/service.go` | ~3 | Adequate (unit tests exist) | Low |
| Boolean NormalizeSObject | `schema_utils.go` | ~6 | Adequate (PR #521 tests) | Low |
| Related list system object filtering | `metadata_layouts.go` | ~15 | Adequate (dedup unit test exists) | Low |
| Lead conversion `inputs[]` wrapper | `standard_actions_handler.go` | ~10 | Partial (contract test) | Low-Med |
| Approval process N+1 fixes | `metadata_deploy_handler.go` | ~20 | Partial | Medium |

**Pre-condition**: `make test-unit` + `make test-integration` pass
**Post-condition**: Run contract tests for affected areas
**Blast radius**: ~8 backend files, 0 frontend files

### Phase 2: Layout + Labels (est. 50 issues closed)

| Fix | Files | Lines Changed | Test Coverage | Risk |
|-----|-------|---------------|---------------|------|
| Layout seed loader (merge templates + live schema) | `bootstrap/layouts.go`, `metadata_layouts.go` | ~80 | **Need new tests first** | Medium-High |
| `SaveLayout` auto-set IsCustomized | `metadata_layouts.go:201-206` | ~2 | Need new test | Low |
| Remove `CleanupHardcodedLayouts` | `layouts.go`, `main.go` | delete ~15 | N/A | Low |
| Label audit for remaining edge cases | `system_tables.json`, data | Varies | Need verification | Medium |

**Pre-condition**: Phase 1 deployed. Write test-first: extend `get_layout_live_test.go` to assert `sections.length >= 3` and human-readable headings (test should FAIL before fix, PASS after).
**Post-condition**: For Account/Contact/Opportunity/Lead/Campaign/Quote: verify `GET /ui-api/layout/{Object}` returns >1 section with human-readable headings.
**Blast radius**: ~5-8 backend files, 0 frontend files

### Phase 3: Dependent Workflow Fixes (est. 9+ issues closed)

| Fix | Files | Lines Changed | Test Coverage | Risk |
|-----|-------|---------------|---------------|------|
| Quote workflow (depends on Phase 2 layouts) | Schema data + frontend | Varies | **None** — need browser verification | High |
| Activity tab (Opportunity/Contact) | Frontend components | ~200+ | None | Medium |

**Pre-condition**: Phase 2 deployed and verified.
**Post-condition**: Manual or E2E browser verification.

---

## Lines Explicitly Excluded

These were investigated and determined to be **NOT worth pursuing now**:

| Item | Why Excluded |
|------|-------------|
| UI API childRelationships enrichment | 0 runtime consumers. Cosmetic type completeness. |
| Chatter Connect API endpoints | Core Chatter works via SObject CRUD. Connect endpoints are nice-to-have. |
| ServiceManager god object refactoring | Correct observation, but touching 100+ handler files is high risk for low immediate value. |
| Key prefix routing (O(1) record type lookup) | Performance optimization, not a functional blocker. Current O(N) works. |
| Incremental metadata cache | Performance optimization. Full reload is fast enough at current scale. |
| Versioned migration framework (#182) | Architectural improvement, not blocking any feature. |

---

## Go/No-Go Assessment

| Decision | Verdict | Rationale |
|----------|---------|-----------|
| Phase 1 (independent fixes) | **GO** | Low risk, high leverage, adequate test coverage |
| Phase 2 (layout seeding) | **GO with test-first** | Must write failing tests before implementing. The "merge templates + live schema" approach resolves the original concern about hiding custom fields. |
| Phase 3 (Quote workflow) | **WAIT** | Depends on Phase 2. Do not start until layouts are verified. |
| Full architecture refactor | **NO-GO** | Not now. Fix the data/config issues first. Architecture improvements are Phase 4+ material. |

---

## Summary: What v1 Got Right vs Wrong

**Right** (confirmed by all 3 lanes):
- Layout resolution always hits Tier 3 — the #1 blocker
- Boolean validation has residual gaps
- Approval process is incomplete
- Test/CI coverage has significant gaps
- 221 issues correctly cataloged with module/type classification

**Wrong** (corrected in v2):
- `splitCamelCase` is broken -> actually works correctly
- Related list dedup missing -> dedup EXISTS, issue is filtering/sorting
- UI API childRelationships is systemic -> cosmetic, 0 runtime consumers
- AutoNumber root cause is frontend -> backend permissions bypass
- #156 OAuth creds is P0 -> already fixed
- `SaveLayout` is safe -> data loss hazard (NOT set IsCustomized)

**The real priority order is**:
1. Load layout seed data (merge approach) + fix SaveLayout IsCustomized
2. Fix autoNumber updateable bypass
3. Fix boolean NormalizeSObject
4. Add related list system object filtering
5. Complete lead conversion format
6. Then tackle remaining workflow fixes
