@ -1,91 +1,50 @@
# ActiveDid Migration Plan - Separate Table Architectur e
# ActiveDid Migration Plan - Implementation Guid e
**Author**: Matthew Raymer
**Author**: Matthew Raymer
**Date**: 2025-08-29T08:03Z
**Date**: 2025-08-29T08:03Z
**Status**: 🎯 **PLANNING** - Active migration planning phase
**Status**: 🎯 **IMPLEMENTATION** - Ready for development
## Objective
## Objective
Move the `activeDid` field from the `settings` table to a dedicated
Move the `activeDid` field from the `settings` table to a dedicated `active_identity` table to improve database architecture, prevent data corruption, and separate identity selection from user preferences.
`active_identity` table to improve database architecture, prevent data corruption,
and separate identity selection from user preferences.
## Result
## Result
This document serves as the comprehensive planning and implementation
This document provides the specific implementation steps required to complete the ActiveDid migration with all necessary code changes.
guide for the ActiveDid migration with enhanced data integrity and
rollback capabilities.
## Use/Run
## Use/Run
Reference this document during implementation to ensure all migration
Follow this implementation checklist step-by-step to complete the migration.
steps are followed correctly and all stakeholders are aligned on the
approach.
## Context & Scope
## Context & Scope
- **In scope** :
- **In scope** : Database migration, API updates, component updates, testing
- Database schema modification for active_identity table with proper constraints
- **Out of scope** : UI changes, authentication flow changes, MASTER_SETTINGS_KEY elimination (future improvement)
- Migration of existing activeDid data with validation
- Updates to PlatformServiceMixin API layer
- Type definition updates
- Testing across all platforms
- Comprehensive rollback procedures
- **Out of scope** :
- Changes to user interface for identity selection
- Modifications to identity creation logic
- Changes to authentication flow
- Updates to individual components (handled by API layer)
## Environment & Preconditions
- **OS/Runtime** : All platforms (Web, Electron, iOS, Android)
- **Versions/Builds** : Current development branch, SQLite database
- **Services/Endpoints** : Local database, PlatformServiceMixin
- **Auth mode** : Existing authentication system unchanged
## Architecture / Process Overview
The migration follows a phased approach to minimize risk and ensure
data integrity with enhanced validation and rollback capabilities:
```mermaid
flowchart TD
A[Current State< br / > activeDid in settings] --> B[Phase 1: Schema Creation< br / > Add active_identity table with constraints]
B --> C[Phase 2: Data Migration< br / > Copy activeDid data with validation]
C --> D[Phase 3: API Updates< br / > Update PlatformServiceMixin methods]
D --> E[Phase 4: Cleanup< br / > Remove activeDid from settings]
E --> F[Final State< br / > Separate active_identity table]
G[Enhanced Rollback Plan< br / > Schema and data rollback] --> H[Data Validation< br / > Verify integrity at each step]
H --> I[Platform Testing< br / > Test all platforms]
I --> J[Production Deployment< br / > Gradual rollout with monitoring]
K[Foreign Key Constraints< br / > Prevent future corruption] --> L[Performance Optimization< br / > Proper indexing]
L --> M[Error Recovery< br / > Graceful failure handling]
```
## Interfaces & Contracts
## Implementation Checklist
### Database Schema Changes
### Phase 1: Database Migration ✅
- [x] Add migration to MIGRATIONS array
- [x] Create active_identity table with constraints
| Table | Current Schema | New Schema | Migration Required |
### Phase 2: API Layer Updates ❌
|-------|----------------|------------|-------------------|
- [ ] Implement `$getActiveIdentity()` method
| `settings` | `activeDid TEXT` | Field removed | Yes - data migration |
- [ ] Update `$accountSettings()` to use new table
| `active_identity` | Does not exist | New table with `activeDid TEXT` + constraints | Yes - table creation |
- [ ] Update `$updateActiveDid()` with dual-write pattern
### Enhanced API Contract Changes
### Phase 3: Component Updates ❌
- [ ] Update 35+ components to use `$getActiveIdentity()`
- [ ] Replace `this.activeDid = settings.activeDid` pattern
- [ ] Test each component individually
| Method | Current Behavior | New Behavior | Breaking Change |
### Phase 4: Testing ❌
|---------|------------------|--------------|-----------------|
- [ ] Test all platforms (Web, Electron, iOS, Android)
| `$accountSettings()` | Returns settings with activeDid | Returns settings with activeDid from new table | No - backward compatible |
- [ ] Test migration rollback scenarios
| `$saveSettings()` | Updates settings.activeDid | Updates active_identity.activeDid | Yes - requires updates |
- [ ] Test data corruption recovery
| `$updateActiveDid()` | Updates internal tracking | Updates active_identity table | Yes - requires updates |
| `$getActiveIdentity()` | Does not exist | New method for active identity management | No - new functionality |
## Repro: End-to-End Procedure
## Required Code Changes
### Phase 1: Enhanced Schema Creation via migration.ts
### 1. Database Migration
```typescript
```typescript
// Add to MIGRATIONS array in src/db-sql/migration.ts
// Add to MIGRATIONS array in src/db-sql/migration.ts
@ -110,68 +69,50 @@ flowchart TD
},
},
```
```
### Phase 2: Enhanced Data Migration with Valid ation
### 2. Missing API Method Implement ation
```typescript
```typescript
// Enhanced migration function with comprehensive validation
// Add to PlatformServiceMixin.ts
async function migrateActiveDidToSeparateTable(): Promise< MigrationResult > {
async $getActiveIdentity(): Promise< { activeDid: string }> {
const result: MigrationResult = {
success: false,
errors: [],
warnings: [],
dataMigrated: 0
};
try {
try {
// 1. Get current activeDid from settings (legacy approach)
const result = await this.$dbQuery(
const currentSettings = await retrieveSettingsForDefaultAccount();
"SELECT activeDid FROM active_identity WHERE id = 1"
const activeDid = currentSettings.activeDid;
if (!activeDid) {
result.warnings.push("No activeDid found in current settings");
return result;
}
// 2. Validate activeDid exists in accounts table
const accountExists = await dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[activeDid]
);
);
if (!accountExists?.values?.length) {
if (result?.values?.length) {
result.errors.push(`ActiveDid ${activeDid} not found in accounts table - data corruption detected`);
const activeDid = result.values[0][0] as string;
return result;
// Validate activeDid exists in accounts
if (activeDid) {
const accountExists = await this.$dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[activeDid]
);
if (accountExists?.values?.length) {
return { activeDid };
} else {
// Clear corrupted activeDid
await this.$dbExec(
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
);
return { activeDid: "" };
}
}
}
}
// 3. Update active_identity table (new system)
await dbExec(
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
[activeDid]
);
// 4. Ensure legacy settings.activeDid stays in sync (backward compatibility)
// This maintains compatibility with IndexedDB migration service
await dbExec(
"UPDATE settings SET activeDid = ? WHERE id = ?",
[activeDid, MASTER_SETTINGS_KEY]
);
dataMigrated = 1;
result.warnings.push(`Successfully migrated activeDid: ${activeDid}`);
return { activeDid: "" };
} catch (error) {
} catch (error) {
result.errors.push(`Migration failed: ${error}` );
logger.error("[PlatformServiceMixin] Error getting active identity:", error);
logger.error("[ActiveDid Migration] Critical error during migration:", error) ;
return { activeDid: "" };
}
}
return result;
}
}
```
```
### Phase 3: Focused API Updates with Dual-Write Pattern
### 3. Updated $accountSettings Method
```typescript
```typescript
// Updated PlatformServiceMixin method - maintains backward compatibility
// Update in PlatformServiceMixin.ts
async $accountSettings(did?: string, defaults: Settings = {}): Promise< Settings > {
async $accountSettings(did?: string, defaults: Settings = {}): Promise< Settings > {
try {
try {
// Get settings without activeDid (unchanged logic)
// Get settings without activeDid (unchanged logic)
@ -182,7 +123,7 @@ async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings>
}
}
// Get activeDid from new table (new logic)
// Get activeDid from new table (new logic)
const activeIdentity = await this._ getActiveIdentity();
const activeIdentity = await this.$ getActiveIdentity();
// Return combined result (maintains backward compatibility)
// Return combined result (maintains backward compatibility)
return { ...settings, activeDid: activeIdentity.activeDid };
return { ...settings, activeDid: activeIdentity.activeDid };
@ -191,164 +132,161 @@ async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings>
return defaults;
return defaults;
}
}
}
}
```
// Enhanced update activeDid method with dual-write pattern
### 4. Component Updates Required
async $updateActiveDid(newDid: string | null): Promise< boolean > {
try {
if (newDid === null) {
// Clear active identity in both tables
await this.$dbExec(
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
);
// Keep legacy field in sync (backward compatibility)
await this.$dbExec(
"UPDATE settings SET activeDid = '' WHERE id = ?",
[MASTER_SETTINGS_KEY]
);
} else {
// Validate DID exists before setting
const accountExists = await this.$dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[newDid]
);
if (!accountExists?.values?.length) {
**35+ components need this pattern change:**
logger.error(`[PlatformServiceMixin] Cannot set activeDid to non-existent DID: ${newDid}`);
return false;
}
// Update active identity in new table
```typescript
await this.$dbExec(
// CURRENT PATTERN (replace in all components):
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
this.activeDid = settings.activeDid || "";
[newDid]
);
// Keep legacy field in sync (backward compatibility)
await this.$dbExec(
"UPDATE settings SET activeDid = ? WHERE id = ?",
[newDid, MASTER_SETTINGS_KEY]
);
}
// Update internal tracking
// NEW PATTERN (use in all components):
await this._updateInternalActiveDid(newDid);
const activeIdentity = await this.$getActiveIdentity();
return true;
this.activeDid = activeIdentity.activeDid || "";
} catch (error) {
logger.error("[PlatformServiceMixin] Error updating activeDid:", error);
return false;
}
}
```
```
### **Master Settings Functions Implementation Strategy**
**Components requiring updates:**
#### **1. Update `retrieveSettingsForDefaultAccount()` **
#### Views (25 components)
- `src/views/DIDView.vue` (line 378)
- `src/views/TestView.vue` (line 654)
- `src/views/ContactAmountsView.vue` (line 226)
- `src/views/HomeView.vue` (line 517)
- `src/views/UserProfileView.vue` (line 185)
- `src/views/ClaimView.vue` (line 730)
- `src/views/OfferDetailsView.vue` (line 435)
- `src/views/QuickActionBvcEndView.vue` (line 229)
- `src/views/SharedPhotoView.vue` (line 178)
- `src/views/ClaimReportCertificateView.vue` (line 56)
- `src/views/ProjectsView.vue` (line 393)
- `src/views/ClaimAddRawView.vue` (line 114)
- `src/views/ContactQRScanShowView.vue` (line 288)
- `src/views/InviteOneAcceptView.vue` (line 122)
- `src/views/RecentOffersToUserView.vue` (line 118)
- `src/views/NewEditProjectView.vue` (line 380)
- `src/views/GiftedDetailsView.vue` (line 443)
- `src/views/ProjectViewView.vue` (line 782)
- `src/views/ContactsView.vue` (line 296)
- `src/views/ContactQRScanFullView.vue` (line 267)
- `src/views/NewActivityView.vue` (line 204)
- `src/views/ClaimCertificateView.vue` (line 42)
- `src/views/ContactGiftingView.vue` (line 166)
- `src/views/RecentOffersToUserProjectsView.vue` (line 126)
- `src/views/InviteOneView.vue` (line 285)
- `src/views/IdentitySwitcherView.vue` (line 202)
- `src/views/AccountViewView.vue` (line 1052)
- `src/views/ConfirmGiftView.vue` (line 549)
- `src/views/ContactImportView.vue` (line 342)
#### Components (10 components)
- `src/components/OfferDialog.vue` (line 177)
- `src/components/PhotoDialog.vue` (line 270)
- `src/components/GiftedDialog.vue` (line 223)
- `src/components/MembersList.vue` (line 234)
- `src/components/OnboardingDialog.vue` (line 272)
- `src/components/ImageMethodDialog.vue` (line 502)
- `src/components/FeedFilters.vue` (line 89)
**Implementation Strategy:**
1. **Systematic Replacement** : Use grep search to find all instances
2. **Pattern Matching** : Replace `this.activeDid = settings.activeDid` with new pattern
3. **Error Handling** : Ensure proper error handling in each component
4. **Testing** : Test each component individually after update
**Example Component Update:**
```typescript
```typescript
// Enhanced implementation with active_identity table integration
// BEFORE (in any component):
export async function retrieveSettingsForDefaultAccount(): Promise< Settings > {
private async initializeSettings() {
const platform = PlatformServiceFactory.getInstance();
const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || "";
// Get settings without activeDid
this.apiServer = settings.apiServer || "";
const sql = "SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " +
}
"finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " +
"lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " +
"lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " +
"notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " +
"passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " +
"showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " +
"webPushServer FROM settings WHERE id = ?";
const result = await platform.dbQuery(sql, [MASTER_SETTINGS_KEY]);
if (!result) {
return DEFAULT_SETTINGS;
} else {
const settings = mapColumnsToValues(result.columns, result.values)[0] as Settings;
// Handle JSON parsing
if (settings.searchBoxes) {
settings.searchBoxes = JSON.parse(settings.searchBoxes);
}
// Get activeDid from separate table
// AFTER (in any component):
const activeIdentityResult = await platform.dbQuery(
private async initializeSettings() {
"SELECT activeDid FROM active_identity WHERE id = 1"
const settings = await this.$accountSettings();
);
const activeIdentity = await this.$getActiveIdentity();
this.activeDid = activeIdentity.activeDid || "";
this.apiServer = settings.apiServer || "";
}
```
if (activeIdentityResult?.values?.length) {
**Alternative Pattern (if settings still needed):**
const activeDid = activeIdentityResult.values[0][0] as string;
if (activeDid) {
// Validate activeDid exists in accounts
const accountExists = await platform.dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[activeDid]
);
if (accountExists?.values?.length) {
settings.activeDid = activeDid;
} else {
logger.warn(`[databaseUtil] ActiveDid ${activeDid} not found in accounts, clearing`);
// Clear corrupted activeDid
await platform.dbExec(
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
);
}
}
}
return settings;
```typescript
}
// If component needs both settings and activeDid:
private async initializeSettings() {
const settings = await this.$accountSettings();
const activeIdentity = await this.$getActiveIdentity();
// Use activeDid from new table
this.activeDid = activeIdentity.activeDid || "";
// Use other settings from settings table
this.apiServer = settings.apiServer || "";
this.partnerApiServer = settings.partnerApiServer || "";
// ... other settings
}
}
```
```
#### **2. Update `$getMergedSettings()` Method**
### 5. Data Migration Function
```typescript
```typescript
// Enhanced implementation with active_identity table integration
// Add to migration.ts
async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise< Settings > {
async function migrateActiveDidToSeparateTable(): Promise< MigrationResult > {
const result: MigrationResult = {
success: false,
errors: [],
warnings: [],
dataMigrated: 0
};
try {
try {
// Get default settings (now without activeDid)
// 1. Get current activeDid from settings (legacy approach)
const defaultSettings = await this.$getSettings(defaultKey, defaultFallback);
const currentSettings = await retrieveSettingsForDefaultAccount();
const activeDid = currentSettings.activeDid;
// If no account DID, return defaults with activeDid from separate table
if (!accountDid) {
if (defaultSettings) {
// Get activeDid from separate table
const activeIdentityResult = await this.$dbQuery(
"SELECT activeDid FROM active_identity WHERE id = 1"
);
if (activeIdentityResult?.values?.length) {
if (!activeDid) {
const activeDid = activeIdentityResult.values[0][0] as string;
result.warnings.push("No activeDid found in current settings");
if (activeDid) {
return result;
// Validate activeDid exists in accounts
const accountExists = await this.$dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[activeDid]
);
if (accountExists?.values?.length) {
defaultSettings.activeDid = activeDid;
} else {
logger.warn(`[Settings Trace] ActiveDid ${activeDid} not found in accounts, clearing`);
// Clear corrupted activeDid
await this.$dbExec(
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
);
}
}
}
}
return defaultSettings || defaultFallback;
}
}
// ... rest of existing implementation for account-specific settings
// 2. Validate activeDid exists in accounts table
const accountExists = await dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[activeDid]
);
if (!accountExists?.values?.length) {
result.errors.push(`ActiveDid ${activeDid} not found in accounts table - data corruption detected`);
return result;
}
// 3. Update active_identity table (new system)
await dbExec(
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
[activeDid]
);
// 4. Ensure legacy settings.activeDid stays in sync (backward compatibility)
await dbExec(
"UPDATE settings SET activeDid = ? WHERE id = ?",
[activeDid, MASTER_SETTINGS_KEY]
);
result.dataMigrated = 1;
result.warnings.push(`Successfully migrated activeDid: ${activeDid}`);
} catch (error) {
} catch (error) {
logger.error(`[Settings Trace] ❌ Failed to get merged settings:`, { defaultKey, accountDid, error });
result.errors.push(`Migration failed: ${error}` );
return defaultFallback;
logger.error("[ActiveDid Migration] Critical error during migration:", error) ;
}
}
return result;
}
}
```
```
@ -377,40 +315,38 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac
- **Hypothesis** : Table needs to be created as part of migration
- **Hypothesis** : Table needs to be created as part of migration
- **Next probe** : Add migration to existing MIGRATIONS array
- **Next probe** : Add migration to existing MIGRATIONS array
- ❌ **Data corruption issues** with orphaned activeDid references
- ❌ **Missing $getActiveIdentity() method** in PlatformServiceMixin
- **Time** : 2025-08-29T08:03Z
- **Evidence** : Method referenced in plan but not implemented
- **Hypothesis** : Method needs to be added to PlatformServiceMixin
- **Next probe** : Implement method with proper error handling
- ❌ **35+ components need updates** to use new API
- **Time** : 2025-08-29T08:03Z
- **Time** : 2025-08-29T08:03Z
- **Evidence** : `IdentitySwitcherView.vue:175` - `hasCorruptedIdentity` detection
- **Evidence** : Grep search found 35+ instances of `this.activeDid = settings.activeDid`
- **Hypothesis** : Current schema allows activeDid to point to non-existent accounts
- **Hypothesis** : All components need to be updated to use `$getActiveIdentity()`
- **Next probe** : Implement foreign key constraints in new table
- **Next probe** : Update each component individually and test
## Risks, Limits, Assumptions
## Risks, Limits, Assumptions
- **Data Loss Risk** : Migration failure could lose activeDid values
- **Data Loss Risk** : Migration failure could lose activeDid values
- **Breaking Changes** : API updates required in PlatformServiceMixin
- **Breaking Changes** : API updates required in PlatformServiceMixin
- **Rollback Complexity** : Schema changes make rollback difficult
- **Testing Overhead** : All platforms must be tested with new structure
- **Testing Overhead** : All platforms must be tested with new structure
- **Performance Impact** : Additional table join for activeDid retrieval
- **Component Updates** : 35+ components need individual updates and testing
- **Migration Timing** : Must be coordinated with other database changes
- **Data Corruption** : Current system has documented corruption issues
- **Foreign Key Constraints** : New constraints may prevent some operations
## Enhanced Rollback Strategy
## Rollback Strategy
### ** Schema Rollback**
### Schema Rollback
```sql
```sql
-- If migration fails, restore original schema
-- If migration fails, restore original schema
DROP TABLE IF EXISTS active_identity;
DROP TABLE IF EXISTS active_identity;
-- Restore activeDid field to settings table if needed
ALTER TABLE settings ADD COLUMN activeDid TEXT;
```
```
### ** Data Rollback**
### Data Rollback
```typescript
```typescript
// Rollback function to restore activeDid to settings table
// Rollback function to restore activeDid to settings table
async function rollbackActiveDidMigration(): Promise< boolean > {
async function rollbackActiveDidMigration(): Promise< boolean > {
try {
try {
// Get activeDid from active_identity table
const activeIdentityResult = await dbQuery(
const activeIdentityResult = await dbQuery(
"SELECT activeDid FROM active_identity WHERE id = 1"
"SELECT activeDid FROM active_identity WHERE id = 1"
);
);
@ -418,7 +354,6 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
if (activeIdentityResult?.values?.length) {
if (activeIdentityResult?.values?.length) {
const activeDid = activeIdentityResult.values[0][0] as string;
const activeDid = activeIdentityResult.values[0][0] as string;
// Restore to settings table
await dbExec(
await dbExec(
"UPDATE settings SET activeDid = ? WHERE id = ?",
"UPDATE settings SET activeDid = ? WHERE id = ?",
[activeDid, MASTER_SETTINGS_KEY]
[activeDid, MASTER_SETTINGS_KEY]
@ -435,142 +370,49 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
}
}
```
```
### **Rollback Triggers**
- Migration validation fails
- Data integrity checks fail
- Performance regression detected
- User reports data loss
- Cross-platform inconsistencies found
## Next Steps
## Next Steps
| Owner | Task | Exit Criteria | Target Date (UTC) |
| Owner | Task | Exit Criteria | Target Date (UTC) |
|-------|------|---------------|-------------------|
|-------|------|---------------|-------------------|
| Development Team | Add migration to existing MIGRATIONS array | Migration script integrated with existing system | 2025-08-30 |
| Development Team | Add migration to MIGRATIONS array | Migration script integrated | 2025-08-30 |
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-08-30 |
| Development Team | Implement $getActiveIdentity() method | Method added to PlatformServiceMixin | 2025-08-30 |
| Development Team | Update PlatformServiceMixin | Core methods updated and tested | 2025-08-31 |
| Development Team | Update $accountSettings method | Method updated and tested | 2025-08-30 |
| Development Team | Implement foreign key constraints | Schema validation prevents corruption | 2025-08-31 |
| Development Team | Update 35+ components | All components use new API | 2025-08-31 |
| QA Team | Platform testing | All platforms tested and verified | 2025-09-01 |
| QA Team | Platform testing | All platforms tested and verified | 2025-09-01 |
| Development Team | Deploy migration | Production deployment successful | 2025-09-02 |
| Development Team | Deploy migration | Production deployment successful | 2025-09-02 |
## Future Improvement: MASTER_SETTINGS_KEY Elimination
**Not critical for this task** but logged for future improvement:
```typescript
// Current: WHERE id = "1"
// Future: WHERE accountDid IS NULL
// This eliminates the confusing concept of "master" settings
// and uses a cleaner pattern for default settings
```
## References
## References
- [Database Migration Guide ](./database-migration-guide.md )
- [Database Migration Guide ](./database-migration-guide.md )
- [Dexie to SQLite Mapping ](./dexie-to-sqlite-mapping.md )
- [Dexie to SQLite Mapping ](./dexie-to-sqlite-mapping.md )
- [PlatformServiceMixin Documentation ](./component-communication-guide.md )
- [PlatformServiceMixin Documentation ](./component-communication-guide.md )
- [Migration Templates ](./migration-templates/ )
## Competence Hooks
## Competence Hooks
- *Why this works* : Separates concerns between identity selection and
- *Why this works* : Separates concerns between identity selection and user preferences, prevents data corruption with foreign key constraints
user preferences, prevents data corruption with foreign key constraints,
- *Common pitfalls* : Forgetting to update all 35+ components, not implementing $getActiveIdentity() method, missing data validation during migration
centralizes identity management through API layer
- *Next skill unlock* : Systematic component updates with grep search and testing
- *Common pitfalls* : Forgetting to implement foreign key constraints, not
- *Teach-back* : Explain why all components need updates and how to systematically find and replace the pattern
testing rollback scenarios, missing data validation during migration,
over-engineering component updates when API layer handles everything
- *Next skill unlock* : Advanced database schema design with constraints,
migration planning with rollback strategies
- *Teach-back* : Explain the four-phase migration approach and why each
phase is necessary, especially the foreign key constraints
## Collaboration Hooks
## Collaboration Hooks
- **Reviewers** : Database team, PlatformServiceMixin maintainers, QA team
- **Sign-off checklist** :
- **Sign-off checklist** :
- [ ] Migration script integrated with existing MIGRATIONS array
- [ ] Migration script integrated with existing MIGRATIONS array
- [ ] Foreign key constraints implemented and tested
- [ ] $getActiveIdentity() method implemented and tested
- [ ] PlatformServiceMixin updated and tested
- [ ] All 35+ components updated to use new API
- [ ] Rollback procedures validated
- [ ] Rollback procedures validated
- [ ] Performance impact assessed
- [ ] All platforms tested
- [ ] All stakeholders approve deployment timeline
- [ ] All stakeholders approve deployment timeline
## Assumptions & Limits
- Current activeDid values are valid and should be preserved
- All platforms can handle the additional database table
- Migration can be completed without user downtime
- Rollback to previous schema is acceptable if needed
- Performance impact of additional table join is minimal
- Foreign key constraints will prevent future corruption
- API layer updates will handle component compatibility
## What Needs to Change
### **1. Database Schema via migration.ts**
- Add migration to existing MIGRATIONS array in `src/db-sql/migration.ts`
- Create `active_identity` table with foreign key constraints
- Add performance indexes
- **Keep `activeDid` field in `settings` table temporarily** for backward compatibility
- **Preserve `MASTER_SETTINGS_KEY = "1"`** for legacy migration support
### **2. PlatformServiceMixin Methods**
- `$accountSettings()` - integrate with new table while maintaining backward compatibility
- `$saveSettings()` - handle activeDid in new table, sync with legacy field
- `$updateActiveDid()` - validate and update new table, sync with legacy field
- `$getActiveIdentity()` - new method for identity management
### **3. Master Settings Functions**
- `retrieveSettingsForDefaultAccount()` - integrate with new table while preserving legacy support
- `$getMergedSettings()` - integrate with new table while preserving legacy support
### **4. Type Definitions**
- **Keep `activeDid` in Settings type temporarily** for backward compatibility
- Create ActiveIdentity type for new table
- Update related interfaces
### **5. Legacy Compatibility**
- **Preserve `MASTER_SETTINGS_KEY = "1"`** for IndexedDB migration service
- **Maintain dual-write pattern** during transition period
- **Ensure legacy clients can still migrate** from Dexie to SQLite
## What Doesn't Need to Change
- **All Vue components** - API layer handles migration transparently
- **Platform services** - Use PlatformServiceMixin, no direct access
- **User interface** - No changes to identity selection UI
- **Authentication flow** - Existing system unchanged
- **Component logic** - All activeDid handling through API methods
- **Migration system** - Use existing migration.ts approach, not separate files
- **IndexedDB migration service** - Must continue working for legacy clients
## Enhanced Architecture: Dual-Write Pattern
### **Phase 1: Add New Table (Current)**
```typescript
// Create active_identity table
// Keep existing settings.activeDid for backward compatibility
// Use dual-write pattern during transition
```
### **Phase 2: Dual-Write Pattern**
```typescript
// When updating activeDid:
// 1. Update active_identity table (new system)
// 2. Update settings.activeDid (legacy compatibility)
// 3. Ensure both stay in sync
```
### **Phase 3: Future Cleanup (Not in Current Scope)**
```typescript
// Eventually:
// 1. Remove activeDid from settings table
// 2. Deprecate MASTER_SETTINGS_KEY
// 3. Use pure accountDid IS NULL pattern
// 4. Update IndexedDB migration service
```
## Backward Compatibility Requirements
### **Critical: IndexedDB Migration Service**
- **Must continue working** for users migrating from Dexie
- **Must recognize `id = "1"`** as master settings
- **Must preserve existing migration paths**
### **Important: Legacy Database Operations**
- **Must continue working** for existing SQLite databases
- **Must handle both old and new patterns**
- **Must not break existing queries**
### **Desired: Cleaner Architecture**
- **New operations** use `accountDid IS NULL` pattern
- **Legacy operations** continue using `MASTER_SETTINGS_KEY`
- **Gradual migration** toward cleaner patterns