# Upstream Merge Conflict Resolution Log
> **Date**: 2026-03-26
> **Merge**: `upstream/main` → `feature/upstream-rebase`
> **Upstream commits**: 214 (test coverage push, typed errors, DI refactor, SOQL parser, useReducer, data/ split, role bootstrap, CI TiDB steps)
> **Our commits**: 37 (TiDB Views, formula DSL, layout CRUD, Lead Convert, Deal/Legal personas, bcrypt-aware password upsert, bootstrap self-healing tests)
> **Total conflicted files**: 34
---
## Table of Contents
1. [.github/workflows/ci.yml](#conflict-1-githubworkflowsciyml)
2. [.gitignore](#conflict-2-gitignore)
3. [backend/cmd/server/main.go](#conflict-3-backendcmdservermain-go)
4. [backend/cmd/server/routes.go](#conflict-4-backendcmdserverroutesgo--7-sub-conflicts)
5. [backend/internal/application/services/service_manager.go](#conflict-5-service_managergo)
6. [backend/internal/application/services/service_manager_init.go](#conflict-6-service_manager_initgo)
7. [backend/internal/bootstrap/permissions.go](#conflict-7-permissionsgo)
8. [backend/internal/bootstrap/system_data.json](#conflict-8-system_datajson--2-sub-conflicts)
9. [backend/internal/features/analytics/dashboard_filtering_test.go](#conflict-9-dashboard_filtering_testgo--modifydelete)
10. [backend/internal/features/analytics/query_helpers.go](#conflict-10-query_helpersgo)
11. [backend/internal/features/automation/executor_helpers.go](#conflict-11-executor_helpersgo)
12. [backend/internal/features/automation/flow_executor.go](#conflict-12-flow_executorgo)
13. [backend/internal/features/metadata/field_create.go](#conflict-13-field_creatego--2-sub-conflicts)
14. [backend/internal/features/metadata/validation_service.go](#conflict-14-validation_servicego--3-sub-conflicts)
15. [backend/internal/features/records/persistence_bulk_insert.go](#conflict-15-persistence_bulk_insertgo)
16. [backend/internal/features/records/persistence_service.go](#conflict-16-persistence_servicego--2-sub-conflicts)
17. [backend/internal/infrastructure/persistence/query_repository.go](#conflict-17-query_repositorygo)
18. [backend/internal/infrastructure/persistence/system_repository.go](#conflict-18-system_repositorygo)
19. [backend/internal/interfaces/rest/query_handler.go](#conflict-19-query_handlergo)
20. [backend/internal/interfaces/rest/sobject_records.go](#conflict-20-sobject_recordsgo--3-sub-conflicts)
21. [backend/internal/interfaces/rest/tooling_query_handler.go](#conflict-21-tooling_query_handlergo--addadd-whole-file)
22. [backend/pkg/expression/engine.go](#conflict-22-expressionenginego)
23. [backend/pkg/expression/walker.go](#conflict-23-expressionwalkergo)
24. [backend/pkg/formula/engine.go](#conflict-24-formulaenginego--3-sub-conflicts)
25. [frontend/src/features/record/components/MetadataRecordDetail.tsx](#conflict-25-metadatarecorddetailtsx--2-sub-conflicts)
26. [frontend/src/features/record/components/MetadataRecordList.tsx](#conflict-26-metadatarecordlisttsx)
27. [frontend/src/generated-schema.ts](#conflict-27-generated-schemats--2-sub-conflicts)
28. [frontend/src/infrastructure/api/data.ts](#conflict-28-datats--modifydelete)
29. [frontend/tsconfig.tsbuildinfo](#conflict-29-tsconfigtsbuildinfo--modifydelete)
30. [shared/pkg/constants/profiles.go](#conflict-30-profilesgo)
31. [shared/pkg/constants/tables.go](#conflict-31-tablesgo)
32. [tests/contract/scenarios/api/lead_convert_test.go](#conflict-32-lead_convert_testgo--addadd)
33. [tests/contract/scenarios/api/object_completeness_test.go](#conflict-33-object_completeness_testgo--addadd-3-sub-conflicts)
34. [tests/contract/scenarios/api/validation_rules_test.go](#conflict-34-validation_rules_testgo--addadd)
---
## Conflict 1: `.github/workflows/ci.yml`
**Type**: content conflict
### 1. Conflict Summary
Go version bump on the integration-test job collides with upstream's addition of two new CI steps (TiDB readiness check and DB bootstrap). Both sides edited the same `go-version` line, and upstream appended two new steps immediately after.
### 2. Upstream Situation
Kept `go-version: '1.22'` but added two new CI steps after Go setup:
- **Wait for TiDB to be ready** — a polling loop with `mysql` client that waits up to 60s for the TiDB service container.
- **Bootstrap database and schema** — runs `go run ./cmd/setup_db` to create tables before integration tests.
### 3. Our Situation
Bumped `go-version` from `'1.22'` to `'1.24'` to match the unit-test job. No other changes to this file.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Integration tests need a running TiDB instance and bootstrapped schema before they can execute. Without the wait step, the integration-test job races against TiDB startup.
- **Ours**: Go 1.24 is required by the module's `go.mod` file; the unit-test job already uses 1.24, so the integration-test job should match.
### 5. Outcome
**Keep both** — accept upstream's two new CI steps AND our `go-version: '1.24'` bump.
### 6. Justification
The CI steps are additive infrastructure that don't conflict with the Go version. The Go version bump is a correctness fix for module compatibility. Both are independently necessary.
---
## Conflict 2: `.gitignore`
**Type**: content conflict
### 1. Conflict Summary
Both sides appended new ignore patterns at the end of the file. The additions are entirely disjoint.
### 2. Upstream Situation
Added 5 lines:
- `.cursor` — AI tooling directory
- `.learnings` — AI learning cache
- `tests/contract/sandbox.yaml` — contract test fixture
- `salesforce.postman_collection.json` — API collection export
### 3. Our Situation
Added ~15 lines:
- Analytics manual/debug test files (`manual_test.go`, `dump_sql_test.go`, formula tests, etc.)
- Migration/data-sync scripts (`sf_data_sync.py`, `import_content_file_metadata.py`, `scripts/sql/`)
- Other internal artifacts
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Prevent AI tooling directories and test fixtures from being committed.
- **Ours**: Prevent internal debug test files and migration scripts from polluting the upstream branch.
### 5. Outcome
**Keep both** — concatenate all ignore patterns from both sides.
### 6. Justification
All entries are disjoint and independently valid. Both sets prevent unintended files from being committed. No overlap or contradiction.
---
## Conflict 3: `backend/cmd/server/main.go`
**Type**: content conflict
### 1. Conflict Summary
Both sides inserted different bootstrap steps at the same insertion point (after metadata cache load, before system data init). The blocks don't overlap logically.
### 2. Upstream Situation
Inserted role initialization:
```go
bootstrap.InitializeRoles(db)
```
Seeds roles before users since users reference roles via foreign key.
### 3. Our Situation
Inserted formula view cache warming:
```go
FormulaViewSvc.RefreshViewCache()
FormulaViewSvc.EnsureViews()
```
Auto-creates missing TiDB Views for objects with active formula/rollup fields and warms the view cache.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Users reference roles via FK — roles must be seeded first or user bootstrap fails with FK violations.
- **Ours**: TiDB Views must exist before queries hit the database or formula fields return NULL.
### 5. Outcome
**Keep both** — role init first (FK dependency), then formula view warming.
### 6. Justification
Independent bootstrap steps with no logical conflict. Ordering: roles must come before users (FK constraint), and views should be created after metadata cache is loaded (needs schema info).
---
## Conflict 4: `backend/cmd/server/routes.go` — 7 sub-conflicts
**Type**: content conflict (multiple regions)
### 1. Conflict Summary
Mix of our-only route additions, upstream's expanded Tooling API routes, API signature changes for handler constructors, route path differences, and superset relationships.
### 2. Upstream Situation
- Expanded Tooling API with `ToolingValidationHandler` (3 ValidationRule CRUD routes) and `ToolingMetadataHandler` (CustomObject create/update, CustomField create, ProfileLayout create) — 7 new routes with 2 new dedicated handlers.
- Changed `NewToolingQueryHandler(svcMgr)` to accept only `ServiceManager` (simplified constructor).
- Changed approval route path to `/process/approvals` (more specific).
### 3. Our Situation
- Added formula-view routes: `POST .../rebuild`, `POST .../rebuild-all`, `GET .../status`, `GET .../ddl`, `POST .../validate` using `rest.NewFormulaViewHandler`.
- Added `POST /actions/standard/convertLead` with `rest.NewStandardActionsHandler`.
- Added layout CRUD endpoints: `HandleListLayouts`, `HandleSaveLayout`, `HandleSetDefaultLayout`, `HandleDeleteLayout`.
- Added `GET /tooling/sobjects/CustomApplication/:id` route.
- Passed explicit sub-services to `NewToolingQueryHandler(metadata, flowInterviewSvc, svcMgr)`.
- Used broader `/process` route group for approvals.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Comprehensive Tooling API shims for Salesforce compatibility testing. Clean constructor signatures.
- **Ours**: Formula view management UI, lead conversion workflow, layout management UI, app inspection.
### 5. Outcome
**Keep both route registrations.** Adopt upstream's `NewToolingQueryHandler(svcMgr)` constructor signature (since we'll merge the tooling handler — see Conflict #21). Keep our formula-view, lead-convert, and layout routes. Keep upstream's expanded tooling routes. Use upstream's `/process/approvals` path. Add our layout management routes under the UI API group.
### 6. Justification
All routes serve different endpoints with no overlap. The constructor signature must match the handler implementation (Conflict #21 determines this). Route paths should follow upstream's more specific naming. Our additional routes are features upstream doesn't have yet.
---
## Conflict 5: `service_manager.go`
**Type**: content conflict (import block)
### 1. Conflict Summary
Our side has a blank line where upstream added two required imports.
### 2. Upstream Situation
Added two imports:
```go
"github.com/nexuscrm/backend/internal/features/uiapi"
pkgerrors "github.com/nexuscrm/backend/pkg/errors"
```
### 3. Our Situation
No additional imports (blank line between `records` and `logging`).
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: The file's struct definition references `uiapi.Service` and `RefreshMetadataCache` uses `pkgerrors.NewInternalError` — both imports are **required** for compilation.
- **Ours**: N/A — we simply didn't have these imports.
### 5. Outcome
**Take upstream** — add both imports.
### 6. Justification
Without these imports the file will not compile. The `uiapi.Service` type is referenced in the struct (line 98) and `pkgerrors.NewInternalError` is called in `RefreshMetadataCache` (line 159).
---
## Conflict 6: `service_manager_init.go`
**Type**: content conflict (architectural divergence)
### 1. Conflict Summary
Fundamentally different approaches to initializing the analytics stack. Upstream uses clean dependency injection; we bypass DI to manually wire `FormulaViewService` as a view resolver into the query path.
### 2. Upstream Situation
Single line using DI container:
```go
sm.QuerySvc = analytics.NewQueryService(sm.deps.QueryRepo, ...)
```
Uses the injected `QueryRepo` from the dependency container. No FormulaView wiring.
### 3. Our Situation
Creates repositories directly from raw DB:
```go
db := sm.db.DB()
queryRepo := persistence.NewQueryRepository(db)
rollupRepo := persistence.NewRollupRepository(db)
sm.FormulaViewSvc = formula.NewFormulaViewService(...)
queryRepo.SetViewResolver(sm.FormulaViewSvc) // route reads through v_{object} views
sm.QuerySvc = analytics.NewQueryService(queryRepo, ...)
```
Bypasses dependency injection to integrate formula views.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Maintainable, testable DI architecture. Services receive their dependencies, not create them.
- **Ours**: Formula View integration requires intercepting query paths to route reads through `v_{object}` views. This requires setting a `ViewResolver` on the `QueryRepository` before the `QueryService` is created.
### 5. Outcome
**Merge both** — use upstream's DI pattern as the base, but add our `FormulaViewService` wiring on top. Instead of `sm.db.DB()`, get the DB handle from `sm.deps` and wire the formula view resolver into the injected `QueryRepo`.
### 6. Justification
We need the DI pattern for testability (upstream has extensive tests that depend on it), but the formula view integration is a core architectural feature that must be preserved. The solution is to call `sm.deps.QueryRepo.SetViewResolver(formulaViewSvc)` after constructing the FormulaViewService.
---
## Conflict 7: `permissions.go`
**Type**: content conflict
### 1. Conflict Summary
Profile list expansion (3 → 5 profiles), dedup tracking, and context.Background() vs timeout context.
### 2. Upstream Situation
Defines 3 profiles: `SystemAdmin`, `StandardUser`, `SalesUser`. No dedup map. Calls `metadata.GetSchemas(context.Background())`.
### 3. Our Situation
Defines 5 profiles: `SystemAdmin`, `StandardUser`, `SalesUser`, `DealUser`, `LegalUser`. Initializes a `seededObjects` dedup map. Calls `metadata.GetSchemas(ctx)` using the function's timeout context.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Basic permission bootstrapping for the three core profiles.
- **Ours**: Extended persona support for deal/legal workflows. Dedup map prevents double-seeding objects. Timeout context prevents hangs during bootstrap.
### 5. Outcome
**Take ours** — keep 5 profiles, dedup map, and timeout context.
### 6. Justification
The rest of this file (outside conflict markers) already references DealUser/LegalUser in the `rules` map and `nonAdminProfiles` list. Taking upstream's 3-profile version would break the rest of the function at runtime. The timeout context is also safer than `context.Background()`.
---
## Conflict 8: `system_data.json` — 2 sub-conflicts
**Type**: content conflict (JSON data)
### 1. Conflict Summary
Two regions: (a) profiles array — different description text + our new profiles; (b) users array — upstream's new fields vs our new users.
### 2. Upstream Situation
**Conflict A (profiles)**: Sales user description changed to `"Standard sales representative profile with CRM access"`. No new profiles.
**Conflict B (users)**: Sales user gains `"username": "sales@test.com"` and `"role_id": "sales_rep"` fields. No new users.
### 3. Our Situation
**Conflict A (profiles)**: Sales user description is `"Sales team access with create and edit permissions"`. Added `deal_user` and `legal_user` profiles.
**Conflict B (users)**: Sales user has `password` and `profile_id`. Added `deal@test.com` (deal_user profile) and `legal@test.com` (legal_user profile).
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Role-based access control with proper `username` and `role_id` fields on user records.
- **Ours**: Expanded persona coverage with dedicated deal and legal users.
### 5. Outcome
**Merge both** — keep our new profiles/users AND adopt upstream's new fields (`username`, `role_id`) on ALL user records (including our new deal/legal users). Use upstream's description text for sales_user.
### 6. Justification
Both changes are data additions. Upstream's new fields (`username`, `role_id`) are expected by the role initialization code (Conflict #3). Our new profiles/users are expected by the permission bootstrap (Conflict #7). All user records must have the new fields for schema consistency.
---
## Conflict 9: `dashboard_filtering_test.go` — modify/delete
**Type**: modify/delete
### 1. Conflict Summary
We deleted an incomplete integration test; upstream modified it (but it's still `t.Skip`-ed as incomplete).
### 2. Upstream Situation
Fleshed out test scaffolding: database connection, temporary table creation, schema field registration. Still has `t.Skip("Incomplete test: dashboard service dependencies not wired up")`.
### 3. Our Situation
Deleted the file entirely during cleanup of non-functional test files.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Preserve and evolve a placeholder for future dashboard filtering integration tests.
- **Ours**: Remove dead code that never runs in CI.
### 5. Outcome
**Keep upstream's version** — restore the file.
### 6. Justification
Upstream actively maintains this test and may complete it in a future PR. Deleting upstream's work-in-progress would create friction on future merges. The `t.Skip` ensures it doesn't affect CI. Cost of keeping it is zero.
---
## Conflict 10: `query_helpers.go`
**Type**: content conflict (architectural)
### 1. Conflict Summary
Formula evaluation removal (ours) vs retention (upstream). This is the core TiDB Views architectural decision point.
### 2. Upstream Situation
Retains Go-level formula evaluation loop inside `Calculate()`:
```go
formulaCtx := formula.Context{User: userMap}
for _, field := range schema.Fields {
    if field.FieldType == "Formula" {
        result, _ := qs.formula.Evaluate(field.Formula, record, formulaCtx)
        record[field.APIName] = result
    }
}
```
Evaluates formula fields in Go at query time.
### 3. Our Situation
Removed the entire formula evaluation loop. Comment explains:
```go
// Formula computation is handled by TiDB Views — no Go-level evaluation needed.
```
After `NormalizeSObject`, immediately returns `record, nil`.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Server-side computed fields at query time. Simple, works without DB-level views.
- **Ours**: Push formula computation to the database layer via TiDB Views for better performance (computed at read time by the DB engine), automatic index usage, and consistency.
### 5. Outcome
**Take ours** — remove the Go-level formula evaluation. Keep our comment.
### 6. Justification
TiDB Views are our core architectural feature. Keeping both evaluation paths would cause double-computation and potential inconsistencies. The view-based approach is strictly superior: DB-level computation, automatic index usage, no N+1 formula evaluation, consistent results across all query paths.
---
## Conflict 11: `executor_helpers.go`
**Type**: content conflict
### 1. Conflict Summary
Different implementations of the same logic: typed-value unwrapping in flow execution's `resolveValue()`.
### 2. Upstream Situation
Inline unwrapping in `resolveValue()`: manually checks the map for `elementReference` (recursively calling `resolveValue`), then `booleanValue`, `stringValue`, `numberValue`. Falls through to return raw value.
### 3. Our Situation
Extracted shared helper `extractTypedValue(value)` (defined at line 457) that unwraps typed-value map objects. Does NOT recursively resolve `elementReference` — relies on the string-reference logic on line 350 to handle it.
### 4. What Problem Is Each Side Trying to Solve?
Both solve the same problem: unwrap typed value union maps (`{stringValue: "foo"}`, `{booleanValue: true}`, `{elementReference: "varName"}`).
- **Upstream**: Explicit recursive resolution inside `resolveValue` itself.
- **Ours**: DRY extraction with downstream resolution.
### 5. Outcome
**Take ours** — keep `extractTypedValue` helper.
### 6. Justification
The extracted helper is DRYer and the behavior is equivalent given the control flow. The `elementReference` string is returned by `extractTypedValue`, then the string-reference check at line 350 calls `resolveValue` for it — achieving the same recursive resolution that upstream does inline.
---
## Conflict 12: `flow_executor.go`
**Type**: content conflict
### 1. Conflict Summary
We added a new `CustomErrors` element handler at the same location where upstream changed the fallback error from `fmt.Errorf` to a typed error.
### 2. Upstream Situation
No `CustomErrors` handler. Changed the fallback error:
```go
return apperrors.NewNotFoundError("flow element", elementName)
```
### 3. Our Situation
Added `CustomErrors` element dispatch:
```go
case flowMeta.CustomErrors != nil:
    return e.executeCustomError()
```
Kept `fmt.Errorf("unknown element: %s", elementName)` for the fallback.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Consistent typed errors across the codebase for better error handling/reporting.
- **Ours**: Support for `CustomError` flow elements (Salesforce Flow Builder feature for user-facing error messages).
### 5. Outcome
**Keep both** — retain our `CustomErrors` handler AND adopt upstream's `apperrors.NewNotFoundError` for the fallback.
### 6. Justification
The CustomErrors feature is needed for Salesforce flow compatibility (flows can raise user-facing errors). The typed error is a quality improvement that should be adopted across the codebase.
---
## Conflict 13: `field_create.go` — 2 sub-conflicts
**Type**: content conflict (architectural)
### 1. Conflict Summary
Two related conflicts: (A) formula validation approach, (B) physical column creation branching.
### 2. Upstream Situation
**Conflict A**: Retains govaluate-based formula validation:
```go
sampleEnv := buildSampleEnv(existingFields)
if err := ms.schemaMgr.ValidateFormula(*field.Formula, sampleEnv); err != nil { ... }
```
**Conflict B**: Unconditionally calls `ms.schemaMgr.AddColumn()` for all field types. Uses `apperrors.NewInternalError()`.
### 3. Our Situation
**Conflict A**: Removed govaluate validation. Comment explains:
```go
// Formula syntax is validated by the /validate endpoint (DSL parser) before creation.
// The old govaluate validator doesn't support cross-table refs or aggregates.
```
**Conflict B**: Added branching for active formula fields:
```go
if isActiveFormula {
    err = ms.schemaMgr.RegisterFieldOnly(ctx, ...) // metadata-only, no physical column
} else {
    err = ms.schemaMgr.AddColumn(ctx, ...)
}
```
Uses `fmt.Errorf`.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Server-side formula syntax validation before field creation. Physical columns for all fields.
- **Ours**: DSL-based formulas supersede govaluate (supports cross-table refs, aggregates, Salesforce syntax). Active formula fields are virtual (backed by TiDB View columns), so physical column creation would be wrong.
### 5. Outcome
**Take ours for the formula validation removal and active-formula branching**, but adopt upstream's `apperrors` error constructors instead of `fmt.Errorf`.
### 6. Justification
The TiDB Views architecture means formula fields are virtual (view columns, not physical columns). Creating physical columns for them would waste storage and create inconsistency. Govaluate validation is superseded by the DSL parser which supports the full Salesforce formula language. Typed errors should be used for consistency with upstream's codebase.
---
## Conflict 14: `validation_service.go` — 3 sub-conflicts
**Type**: content conflict (API signature + logic)
### 1. Conflict Summary
Three regions: (A) `ValidateRecord` method signature, (B) pattern validation error return, (C) formula context construction for validation rules.
### 2. Upstream Situation
**Conflict A**: `ValidateRecord` accepts `currentUser *models.UserSession`.
**Conflict B**: Adds `return apperrors.NewValidationError(field.APIName, message)` after the pattern check — but this return appears to be placed incorrectly (returns error even when pattern matches — likely a bug).
**Conflict C**: Builds user context inline from `currentUser`:
```go
"Profile": map[string]interface{}{"Name": currentUser.ProfileName},
"UserRole": map[string]interface{}{"Name": currentUser.RoleName},
```
Does NOT coerce the prior record.
### 3. Our Situation
**Conflict A**: `ValidateRecord` accepts `userContext map[string]interface{}` (pre-built map).
**Conflict B**: No additional return — only returns error on pattern mismatch (correct behavior).
**Conflict C**: Uses pre-built `userContext` parameter with `__Profile`, `__UserRole`, `__User` keys. Coerces prior record with `coerceForFormulaEval()` for correct `ISCHANGED` comparisons.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Self-contained validation with typed session parameter. Simple inline context construction.
- **Ours**: Reusable user context construction (called from multiple sites). Correct ISCHANGED/PRIORVALUE behavior with coerced prior records (numeric strings → floats, date strings → time objects).
### 5. Outcome
**Take ours** — keep `map[string]interface{}` signature, coerced prior records, and correct pattern validation logic. Adopt upstream's `apperrors.NewValidationError` return types.
### 6. Justification
The pre-built user context pattern is used by multiple callers (persistence_service, bulk_insert) — changing the signature cascades to all call sites. Our prior-record coercion is needed for correct ISCHANGED behavior (e.g., `ISCHANGED(Amount)` must compare floats, not string representations). Upstream's pattern validation return appears buggy.
---
## Conflict 15: `persistence_bulk_insert.go`
**Type**: content conflict (cascading)
### 1. Conflict Summary
Direct cascading from Conflict #14 (ValidateRecord signature).
### 2. Upstream Situation
Passes `currentUser` (`*models.UserSession`) directly to `ValidateRecord`.
### 3. Our Situation
Calls `ps.buildUserContext(ctx, currentUser)` to construct user context map, passes it to `ValidateRecord`.
### 4. What Problem Is Each Side Trying to Solve?
Same as Conflict #14 — different API signatures.
### 5. Outcome
**Take ours** — consistent with our ValidateRecord signature choice.
### 6. Justification
Direct consequence of Conflict #14 resolution. The call site must match the signature.
---
## Conflict 16: `persistence_service.go` — 2 sub-conflicts
**Type**: content conflict (imports + cascading)
### 1. Conflict Summary
Two regions: (A) import differences, (B) cascading ValidateRecord signature.
### 2. Upstream Situation
**Conflict A**: Adds `appErrors "github.com/nexuscrm/backend/pkg/errors"` and `"github.com/nexuscrm/backend/pkg/formula"` imports.
**Conflict B**: Passes `currentUser` directly to `ValidateRecord`.
### 3. Our Situation
**Conflict A**: Neither import present. Formula moved to TiDB Views, error references refactored.
**Conflict B**: Calls `ps.buildUserContext(txCtx, currentUser)` and passes `userCtx`.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: `appErrors` is used throughout the file (lines 94, 199, 312, etc.). `formula` is used for Go-level formula evaluation.
- **Ours**: Removed `formula` because evaluation moved to TiDB Views. Apparently lost `appErrors` during cleanup (this is a bug on our side — the import is needed).
### 5. Outcome
**Keep `appErrors` import** (still used in the file), **drop `formula` import** (no longer needed). Keep our `buildUserContext` call pattern.
### 6. Justification
`appErrors` is referenced throughout the file — removing it causes a compilation failure. `formula` is only needed for Go-level formula evaluation, which we've moved to DB views. The ValidateRecord call pattern is consistent with Conflict #14.
---
## Conflict 17: `query_repository.go`
**Type**: content conflict (imports)
### 1. Conflict Summary
Both sides added a different import at the same position in the import block.
### 2. Upstream Situation
Added `pkgerrors "github.com/nexuscrm/backend/pkg/errors"` — used for typed error constructors throughout the file.
### 3. Our Situation
Added `"github.com/nexuscrm/backend/internal/domain/ports"` — used for `ports.ViewResolver` interface (lines 27, 37) for TiDB View resolution.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Typed errors for better error classification.
- **Ours**: View resolver interface for formula view query routing.
### 5. Outcome
**Keep both imports**.
### 6. Justification
Both are used in the file and serve completely different purposes. Additive, non-overlapping.
---
## Conflict 18: `system_repository.go`
**Type**: content conflict (SQL logic)
### 1. Conflict Summary
Different `ON DUPLICATE KEY UPDATE` clause in `BatchUpsertUsers` — conditional vs unconditional password overwrite.
### 2. Upstream Situation
Unconditionally overwrites all fields:
```sql
Password=VALUES(Password)
```
### 3. Our Situation
Conditional password update:
```sql
Password=IF(Password LIKE '$2%%', Password, VALUES(Password))
```
Only overwrites the password if the existing value is NOT already a bcrypt hash.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Simple, predictable upsert behavior.
- **Ours**: Idempotent bootstrap — prevents overwriting already-hashed passwords with seed plaintext values during re-runs.
### 5. Outcome
**Take ours** — keep the bcrypt-aware conditional.
### 6. Justification
Without this, re-running bootstrap overwrites bcrypt-hashed passwords with the seed plaintext values, breaking login for any user whose password was already hashed. This is both a correctness fix and a security improvement. Upstream's version silently breaks authentication on re-bootstrap.
---
## Conflict 19: `query_handler.go`
**Type**: content conflict (add/add at end of file)
### 1. Conflict Summary
Two completely different functions added at the same location (end of file).
### 2. Upstream Situation
Added `normalizeSOQLParseError()` — normalizes SOQL parse error messages (e.g., converts "unexpected keyword" to "unexpected token").
### 3. Our Situation
Added `hydrateQueryLookups()` — iterates reference fields on query results, fetches target records, populates `_Name` fields (Name, Username fallback).
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Better error messages for SOQL parse failures in the API response.
- **Ours**: Lookup name hydration for query results (so reference fields show names, not just IDs).
### 5. Outcome
**Keep both functions**.
### 6. Justification
Both are called elsewhere in the file. `normalizeSOQLParseError` is called on lines 52 and 178; `hydrateQueryLookups` is called on lines 103 and 216. They serve completely different purposes with no overlap.
---
## Conflict 20: `sobject_records.go` — 3 sub-conflicts
**Type**: content conflict (algorithmic divergence)
### 1. Conflict Summary
Three related conflicts in the `hydrateLookups` function: type definition, field iteration logic, and fetching strategy. The core divergence is N+1 queries (ours) vs batched queries (upstream).
### 2. Upstream Situation
- **Conflict A (type)**: Introduces `lookupRef` struct for batching (`fieldAPIName`, `targetObj`, `targetID`).
- **Conflict B (iteration)**: Guard clause pattern, handles polymorphic fields (`IsPolymorphic` check), collects `lookupRef` entries grouped by target object.
- **Conflict C (fetching)**: Batch-fetches per unique target object via `h.svcMgr.Persistence.GetMany(ctx, objName, ids)`. Builds cache map, second pass for assignment.
### 3. Our Situation
- **Conflict A (type)**: Simple one-line comment, no struct.
- **Conflict B (iteration)**: Nested `if` pattern, inline dedup check. No polymorphic handling.
- **Conflict C (fetching)**: Individual `Persistence.Get(ctx, targetObj, idStr)` per field per record. Direct assignment.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Performance — O(k) queries instead of O(n×k). Polymorphic reference support (e.g., `WhoId` which could be Lead or Contact).
- **Ours**: Same functional goal, simpler implementation.
### 5. Outcome
**Take upstream's batched approach**.
### 6. Justification
The batched approach is strictly better on performance (fewer DB queries, especially on large result sets). Polymorphic field handling is needed for Salesforce compatibility (fields like `WhoId`, `WhatId` can reference different object types). Our simple N+1 implementation was interim.
---
## Conflict 21: `tooling_query_handler.go` — add/add (whole file)
**Type**: add/add (entire file conflict)
### 1. Conflict Summary
Both sides independently created this file with fundamentally different architectures for the Tooling API query handler.
### 2. Upstream Situation
Lean, SOQL-parser-based implementation:
- Reuses `soql.ParseSOQL` to parse the query.
- Translates table/field names via `toolingTableAliases` / `toolingFieldAliases` maps.
- Delegates to `QueryService.Query` for execution.
- Only depends on `ServiceManager`.
- Currently handles `ValidationRule` aliasing; other objects work if they exist as internal tables.
### 3. Our Situation
Feature-rich, regex-based implementation:
- Manually parses SOQL with compiled regexes (`fromRe`, `whereFieldRe`, `inListRe`, `eqRe`).
- Uses `switch` on object name to dispatch to dedicated handlers: `FlowDefinition`, `Flow`, `CustomApplication`, `ValidationRule`, `FlowTestCoverage`.
- Depends on `metadata.MetadataService`, `automation.FlowInterviewService`, and `services.ServiceManager`.
- Includes `parseInList` utility.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Clean architecture — reuse the existing SOQL parser, single responsibility, easy to extend via alias maps, fewer dependencies.
- **Ours**: Support for 5 Tooling API object types with specialized query handling (some objects like FlowDefinition aren't backed by regular tables).
### 5. Outcome
**Start with upstream's SOQL-parser-based architecture**, then extend it with our additional Tooling object types. Add entries to the alias maps for objects that map to internal tables. For objects that require specialized handling (e.g., FlowDefinition which isn't a table), add dedicated handler methods that the main handler dispatches to before falling through to the generic SOQL path.
### 6. Justification
Upstream's approach is architecturally superior: it reuses the parser (no regex duplication), has fewer dependencies, is more maintainable, and is testable. Our regex-based approach is fragile and won't scale to more object types. However, we need support for FlowDefinition, Flow, CustomApplication, and FlowTestCoverage — these should be ported as specialized handlers within upstream's architecture.
---
## Conflict 22: `expression/engine.go`
**Type**: content conflict (overlapping expansion)
### 1. Conflict Summary
Both sides expanded the expression engine with Salesforce formula functions. Upstream added 3 functions; we added 20+. The three upstream functions overlap with ours but use different implementations.
### 2. Upstream Situation
Added 3 functions with strict helpers:
- `NOT` using `toBool()` (strict boolean conversion)
- `ISBLANK` using `isBlank()` (nil/empty/zero check)
- `ISNULL` using `isBlank()`
### 3. Our Situation
Added 20+ functions with permissive `truthy()` helper:
- `OR`, `AND`, `NOT`, `TRUTHY`
- `BEGINS`, `FIND`, `SUBSTITUTE`
- `ADDMONTHS`, `DATEVALUE`, `HYPERLINK`
- `ISBLANK`, `ISNULL`, `ISPICKVAL`, `TEXT`
- `CASE`, `LEFT`, `RIGHT`, `MID`, `CONTAINS`
- `YEAR`, `MONTH`, `CEILING`, `ABS`, `MOD`, `NULLVALUE`
Note: HEAD also has duplicate `ISBLANK`/`ISNULL` definitions later in the file (lines 326–344) that collide with upstream's versions.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Core boolean functions for validation rule evaluation. Strict type handling.
- **Ours**: Comprehensive Salesforce formula language coverage for formula fields and complex validation rules.
### 5. Outcome
**Take ours** (superset of functions). Adopt upstream's stricter `toBool()`/`isBlank()` helpers specifically for `NOT`, `ISBLANK`, and `ISNULL`. Remove the duplicate registrations.
### 6. Justification
We need the full function library for formula fields and validation rules. Upstream's strict helpers are better for the boolean/null functions (avoid truthy coercion surprises like `0` being treated as `false`), but the rest of our 20+ functions are needed for production Salesforce formula compatibility.
---
## Conflict 23: `expression/walker.go`
**Type**: content conflict
### 1. Conflict Summary
We added a `contains` binary operator case; upstream changed the error type for the `in` operator's fallback.
### 2. Upstream Situation
Uses `apperrors.NewValidationError("expression", "contains/in operator requires string literal on right side")` for the error. No separate `contains` case — mentions "contains/in" in the error message.
### 3. Our Situation
Added explicit `case "contains"` handler generating `LIKE '%…%'` SQL:
```go
case "contains":
    w.builder.WriteString("LIKE ?")
    w.args = append(w.args, "%"+strArg.Value+"%")
```
Uses `fmt.Errorf` for errors.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Typed errors for expression evaluation. Combined error message for future contains support.
- **Ours**: Actual SQL `CONTAINS` operator support for Salesforce formula compatibility.
### 5. Outcome
**Keep both** — retain our `contains` case AND adopt upstream's `apperrors.NewValidationError` for all error returns in this function.
### 6. Justification
`CONTAINS` is needed for Salesforce formula compatibility (used in validation rules and formula fields). Upstream's error message anticipated the need but didn't implement it. Typed errors improve error handling consistency.
---
## Conflict 24: `formula/engine.go` — 3 sub-conflicts
**Type**: content conflict (architectural)
### 1. Conflict Summary
Three regions: (A) imports, (B) evaluation preprocessing pipeline, (C) helper functions. Fundamentally different approaches to formula preprocessing.
### 2. Upstream Situation
**Conflict A**: Imports only `"regexp"`.
**Conflict B**: Two-step preprocessing:
```go
expression = preprocessFormula(expression, env)     // ISCHANGED/PRIORVALUE macros
expression = preprocessUserVars(expression, env, ctx.User) // $User/$Profile/$UserRole
result, err := exprEngine.Evaluate(expression, env)
```
**Conflict C**: Two focused preprocessors: `preprocessFormula` (ISCHANGED/PRIORVALUE only) and `preprocessUserVars` ($User/$Profile/$UserRole resolution).
### 3. Our Situation
**Conflict A**: Imports `"fmt"`, `"regexp"`, `"strings"`.
**Conflict B**: Single comprehensive step with `_has_prior_` sentinel:
```go
env["_has_prior_"] = (priorRecord != nil) // sentinel for insert vs update
expression = normalizeFormulaExpression(expression)
result, err := exprEngine.Evaluate(expression, env)
```
**Conflict C**: Comprehensive normalization pipeline `normalizeFormulaExpression` handling:
- Block comments removal
- Whitespace normalization
- Typed relation syntax (`Account.Name` → `Account__Name`)
- `$Var` → `__Var` prefix transformation
- Boolean/null normalization
- Function name case normalization
- ISCHANGED/PRIORVALUE macro rewriting
- Salesforce operator replacement (`&`→`+`, `<>`→`!=`, `=`→`==`)
- Boolean comparison rewriting
- String literal protection during regex rewrites
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Minimal preprocessing for the two most common formula patterns (ISCHANGED/PRIORVALUE and user variable resolution).
- **Ours**: Full Salesforce formula syntax compatibility. Real-world Salesforce formulas use `&` for concatenation, `<>` for inequality, block comments, typed relation syntax (`Account.Name`), case-insensitive function names, etc.
### 5. Outcome
**Take ours** — keep the comprehensive normalization pipeline and `_has_prior_` sentinel.
### 6. Justification
Upstream's preprocessors are a strict subset of what our normalizer handles. Our pipeline covers real-world Salesforce formulas observed in production orgs. Without operator replacement, formulas like `FirstName & " " & LastName` would fail. Without block comments, formulas with `/* ... */` would fail. The `_has_prior_` sentinel correctly handles insert contexts (all prior values are nil, ISCHANGED returns false).
---
## Conflict 25: `MetadataRecordDetail.tsx` — 2 sub-conflicts
**Type**: content conflict (state management refactor)
### 1. Conflict Summary
Two regions: (A) scroll-to-related-list `useEffect`, (B) state declarations (`useState` vs `useReducer`).
### 2. Upstream Situation
**Conflict A**: Simple single-pass scroll logic using `dispatch(SET_SCROLL_TARGET)` via the reducer.
**Conflict B**: Empty — all state was consolidated into `useReducer` + `DetailState` (declared at lines 38–94 above the conflict).
### 3. Our Situation
**Conflict A**: Robust retry mechanism — up to 5 `requestAnimationFrame` attempts to find the target element, with cancellation cleanup. Uses `setScrollTarget(null)` (a `useState` setter).
**Conflict B**: Declares ~12 individual `useState` hooks (loading, error, refreshKey, modal booleans, deleteLoading, etc.).
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Cleaner state management with reducer pattern — fewer re-renders, centralized state transitions, easier to reason about.
- **Ours**: Reliable scroll-to-related-list in slow-rendering scenarios (element may not exist on first render pass).
### 5. Outcome
**Take upstream's `useReducer` architecture**. Port our scroll retry logic to use `dispatch` instead of `setScrollTarget`.
### 6. Justification
The `useReducer` refactor is a structural improvement that reduces state management complexity across a large component. Our scroll retry logic is a UX improvement that can be adapted to work with the reducer — replace `setScrollTarget(null)` with `dispatch({type: 'SET_SCROLL_TARGET', payload: null})`.
---
## Conflict 26: `MetadataRecordList.tsx`
**Type**: content conflict (state management refactor)
### 1. Conflict Summary
Same pattern as Conflict #25: `useState` hooks vs `useReducer` + `useCallback` dispatch helpers.
### 2. Upstream Situation
Provides `useCallback` dispatch helpers (`setSelectedRecords`, `setLocalSearch`, `setActiveFilterExpr`, `setViewMode`, `setSelectedFieldNames`) wrapping the reducer declared above (lines 91–154).
### 3. Our Situation
Declares individual `useState` hooks for `localSearch`, `debouncedSearch`, `createObjectWizardOpen`, `bulkEditModalOpen`, `selectFieldsModalOpen`, `selectedFieldNames`, `bulkDeleteConfirmOpen`, `bulkDeleteLoading`, `page`, `pageSize`, `activeFilterExpr`, `viewMode`.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Same reducer-based architecture as MetadataRecordDetail.
- **Ours**: Direct state management. Notably includes `page`/`pageSize` for server-side pagination (our feature).
### 5. Outcome
**Take upstream's `useReducer` pattern**. Ensure `page`/`pageSize` state is incorporated into the reducer (may need to add `SET_PAGE`/`SET_PAGE_SIZE` action types if not already present).
### 6. Justification
Same reasoning as #25 — reducer pattern is structurally better. Our `page`/`pageSize` state needs to be preserved as part of the reducer since server-side pagination is a feature upstream doesn't have yet.
---
## Conflict 27: `generated-schema.ts` — 2 sub-conflicts
**Type**: content conflict (comments only)
### 1. Conflict Summary
JSDoc comment wording differences on two auto-generated interfaces.
### 2. Upstream Situation
- `SystemPermissionSetPermission`: `"Per-permission-set permission assignments"`
- `SystemProfilePermission`: `"Per-profile permission assignments"`
### 3. Our Situation
- `SystemPermissionSetPermission`: `"Junction table linking permission sets to system permissions"`
- `SystemProfilePermission`: `"Junction table linking profiles to system permissions"`
### 4. What Problem Is Each Side Trying to Solve?
Both describe the same interfaces with different phrasing. This file is auto-generated by codegen.
### 5. Outcome
**Take upstream** for consistency.
### 6. Justification
This file is auto-generated. Comments will be overwritten on next codegen run. Upstream's phrasing is more concise.
---
## Conflict 28: `data.ts` — modify/delete
**Type**: modify/delete
### 1. Conflict Summary
Upstream deleted the monolithic `data.ts` (~830 lines) and split it into `data/` sub-modules. We continued modifying `data.ts` in place with new features.
### 2. Upstream Situation
Deleted `data.ts`. Created `data/` directory with focused sub-modules:
- `data/query.ts` — SOQL queries, paged queries
- `data/crud.ts` — get/create/update/delete record
- `data/search.ts` — suggestions, SOSL, global search
- `data/collections.ts` — sObject Collections (bulk operations)
- `data/recycleBin.ts` — recycle bin with progressive loading
- `data/analyticsData.ts` — count, sum, group_by
- `data/actions.ts` — execute actions, notifications
- `data/index.ts` — re-exports dataAPI facade
### 3. Our Situation
Kept `data.ts` monolithic. Added:
- `convertLead` standard action
- Improved `getRichRecord` (UI API with fallback)
- Expanded sObject Collections API
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Maintainability — 830-line monolith split into ~8 focused modules.
- **Ours**: New features (lead convert, rich record fetching) added to existing module.
### 5. Outcome
**Take upstream's split architecture.** Delete `data.ts`. Port our unique additions into the appropriate sub-modules:
- `convertLead` → `data/actions.ts`
- `getRichRecord` improvements → `data/crud.ts`
- Collections API changes → `data/collections.ts`
### 6. Justification
The split is a clean architecture improvement. Our additions are functional, not structural — they can be placed in the correct sub-modules without losing any functionality. Keeping the monolith would mean future merges with upstream will always conflict on this file.
---
## Conflict 29: `tsconfig.tsbuildinfo` — modify/delete
**Type**: modify/delete
### 1. Conflict Summary
We deleted this TypeScript build cache artifact; upstream has an updated version.
### 2. Upstream Situation
Updated `tsconfig.tsbuildinfo` with new file entries reflecting upstream's codebase changes. Generated by TypeScript 5.9.3.
### 3. Our Situation
Deleted the file during build artifact cleanup.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Committed build artifact (likely unintentional).
- **Ours**: Clean up build artifacts that shouldn't be in version control.
### 5. Outcome
**Delete it** (take ours). Confirm it's in `.gitignore` (handled by Conflict #2).
### 6. Justification
This is an auto-generated TypeScript build cache file. It should never be committed — it changes on every build, creates noise in diffs, and varies between developer machines.
---
## Conflict 30: `profiles.go`
**Type**: content conflict (add vs no-change)
### 1. Conflict Summary
We added two new profile constants; upstream has no changes in this region.
### 2. Upstream Situation
No additions (empty between markers).
### 3. Our Situation
Added:
```go
ProfileDealUser = "deal_user"
ProfileLegalUser = "legal_user"
```
### 4. What Problem Is Each Side Trying to Solve?
- **Ours**: Typed constants for the new deal/legal personas added in Conflicts #7 and #8.
### 5. Outcome
**Take ours** — keep the new profile constants.
### 6. Justification
Referenced throughout our permission bootstrap code and system data. Without these constants, the code would use string literals, defeating the purpose of the constants package.
---
## Conflict 31: `tables.go`
**Type**: content conflict (add vs no-change)
### 1. Conflict Summary
We added 6 field constants for permission tables; upstream has no changes in this region.
### 2. Upstream Situation
No additions.
### 3. Our Situation
Added 6 field constants:
```go
FieldProfilePermission_ProfileID
FieldProfilePermission_PermissionAPIName
FieldProfilePermission_IsEnabled
FieldPermissionSetPermission_PermissionSetID
FieldPermissionSetPermission_PermissionAPIName
FieldPermissionSetPermission_IsEnabled
```
### 4. What Problem Is Each Side Trying to Solve?
- **Ours**: Typed field constants for ProfilePermission and PermissionSetPermission tables, replacing string literals in permission bootstrap code.
### 5. Outcome
**Take ours** — keep the new field constants.
### 6. Justification
These constants are used by the permission bootstrap code that references them by name rather than string literals. Consistent with the codebase's pattern of typed constants for all system tables.
---
## Conflict 32: `lead_convert_test.go` — add/add
**Type**: add/add
### 1. Conflict Summary
Different test setup strategies for ensuring the Lead object exists before testing conversion.
### 2. Upstream Situation
Inline verification: calls `GET /sobjects/Lead/describe` and fatals with a descriptive message if 404. Does NOT include `"Name"` in the create payload.
### 3. Our Situation
Uses `ensureLead(t)` bootstrap helper to guarantee the Lead object and its fields exist. Includes `"Name"` in the create payload alongside `"LastName"`.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Verify Lead exists before testing; fail fast with clear message if not.
- **Ours**: Self-healing — create the Lead object if missing, then proceed.
### 5. Outcome
**Take ours** — keep the bootstrap self-healing pattern.
### 6. Justification
The bootstrap pattern (`ensureLead`) is more robust: it creates the Lead object if missing rather than just fataling. This makes the test runnable in clean environments without manual setup. Consistent with our self-healing test philosophy across all contract tests.
---
## Conflict 33: `object_completeness_test.go` — add/add (3 sub-conflicts)
**Type**: add/add
### 1. Conflict Summary
Three regions: (A) bootstrap call, (B) resource cleanup, (C) assertion message wording.
### 2. Upstream Situation
**Conflict A**: No bootstrap — test expects the object to already exist.
**Conflict B**: Adds `defer resp.Body.Close()` for proper resource cleanup.
**Conflict C**: Descriptive assertion: `"FINDING: CampaignMemberStatus object is missing (status %d): %s. This breaks Campaign Layout configuration."`
### 3. Our Situation
**Conflict A**: Calls `ensureCampaignMemberStatus(t)` to bootstrap.
**Conflict B**: No `defer resp.Body.Close()`.
**Conflict C**: Simple assertion: `"CampaignMemberStatus object should exist after bootstrap (status %d): %s"`
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Discover missing objects and report findings with business context. Clean resource handling.
- **Ours**: Self-healing bootstrap — create the object if missing.
### 5. Outcome
**Take ours for bootstrap** (Conflict A) but **add upstream's `defer resp.Body.Close()`** (Conflict B) and adopt upstream's descriptive assertion messages (Conflict C).
### 6. Justification
Bootstrap self-healing is our test philosophy. But `resp.Body.Close()` is a genuine resource leak fix. Upstream's descriptive messages ("This breaks Campaign Layout configuration") add valuable debugging context.
---
## Conflict 34: `validation_rules_test.go` — add/add
**Type**: add/add
### 1. Conflict Summary
We add `ensureObject(t, ...)` bootstrap call before creating a validation rule; upstream doesn't.
### 2. Upstream Situation
No bootstrap — assumes the target object already exists.
### 3. Our Situation
Adds `ensureObject(t, objectName, objectName, objectName+"s")` to create the target object if missing.
### 4. What Problem Is Each Side Trying to Solve?
- **Upstream**: Assumes environment is pre-configured.
- **Ours**: Self-healing — test works in clean environments.
### 5. Outcome
**Take ours** — keep the bootstrap call.
### 6. Justification
Consistent with our self-healing test pattern used across all contract tests (Conflicts #32, #33). Without it, the test fails in clean environments where the object doesn't exist yet.
---
## Summary by Resolution Pattern
| Pattern | Conflicts | Count |
|---------|-----------|-------|
| **Keep both sides** (additive) | #1, #2, #3, #4, #8, #12, #17, #19 | 8 |
| **Take ours** (our feature prevails) | #7, #10, #11, #13, #14, #15, #22, #23, #24, #30, #31, #32, #34 | 13 |
| **Take upstream** (upstream is better) | #5, #9, #20, #25, #26, #27, #29 | 7 |
| **Merge architecturally** (combine approaches) | #6, #21, #28 | 3 |
| **Take ours + upstream fixes** | #16, #18, #33 | 3 |
## Key Architectural Decisions
1. **TiDB Views stay** — Go-level formula evaluation removed (Conflicts #10, #13, #24)
2. **Upstream DI pattern adopted** — but extended for formula view wiring (Conflict #6)
3. **Upstream useReducer adopted** — our features ported to reducer (Conflicts #25, #26)
4. **Upstream batched hydration adopted** — replaces our N+1 approach (Conflict #20)
5. **Upstream SOQL-parser tooling handler adopted** — our object types ported in (Conflict #21)
6. **Upstream data/ split adopted** — our additions ported to sub-modules (Conflict #28)
7. **Our ValidateRecord signature kept** — pre-built user context map, coerced prior records (Conflict #14)
8. **Our bcrypt-aware password upsert kept** — security/idempotency fix (Conflict #18)
9. **Our bootstrap self-healing tests kept** — consistent test philosophy (Conflicts #32, #33, #34)
10. **Our expanded profiles/personas kept** — Deal/Legal users (Conflicts #7, #8, #30, #31)
