forked from jsnbuchanan/crowd-funder-for-time-pwa
chore: update plan for handling MASTER_SETTINGS_KEY
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# ActiveDid Migration Plan - Separate Table Architecture
|
# ActiveDid Migration Plan - Separate Table Architecture
|
||||||
|
|
||||||
**Author**: Matthew Raymer
|
**Author**: Matthew Raymer
|
||||||
**Date**: 2025-08-29T07:24Z
|
**Date**: 2025-08-29T08:03Z
|
||||||
**Status**: 🎯 **PLANNING** - Active migration planning phase
|
**Status**: 🎯 **PLANNING** - Active migration planning phase
|
||||||
|
|
||||||
## Objective
|
## Objective
|
||||||
@@ -85,29 +85,35 @@ flowchart TD
|
|||||||
|
|
||||||
## Repro: End-to-End Procedure
|
## Repro: End-to-End Procedure
|
||||||
|
|
||||||
### Phase 1: Enhanced Schema Creation
|
### Phase 1: Enhanced Schema Creation via migration.ts
|
||||||
|
|
||||||
```sql
|
```typescript
|
||||||
-- Create new active_identity table with proper constraints
|
// Add to MIGRATIONS array in src/db-sql/migration.ts
|
||||||
CREATE TABLE active_identity (
|
{
|
||||||
|
name: "003_active_did_separate_table",
|
||||||
|
sql: `
|
||||||
|
-- Create new active_identity table with proper constraints
|
||||||
|
CREATE TABLE IF NOT EXISTS active_identity (
|
||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
activeDid TEXT NOT NULL,
|
activeDid TEXT NOT NULL,
|
||||||
lastUpdated TEXT NOT NULL DEFAULT (datetime('now')),
|
lastUpdated TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
FOREIGN KEY (activeDid) REFERENCES accounts(did) ON DELETE CASCADE
|
FOREIGN KEY (activeDid) REFERENCES accounts(did) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Add performance indexes
|
-- Add performance indexes
|
||||||
CREATE INDEX IF NOT EXISTS idx_active_identity_activeDid ON active_identity(activeDid);
|
CREATE INDEX IF NOT EXISTS idx_active_identity_activeDid ON active_identity(activeDid);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_active_identity_single_record ON active_identity(id);
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_active_identity_single_record ON active_identity(id);
|
||||||
|
|
||||||
-- Insert default record (will be updated during migration)
|
-- Insert default record (will be updated during migration)
|
||||||
INSERT INTO active_identity (id, activeDid, lastUpdated) VALUES (1, '', datetime('now'));
|
INSERT OR IGNORE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, '', datetime('now'));
|
||||||
|
`,
|
||||||
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 2: Enhanced Data Migration with Validation
|
### Phase 2: Enhanced Data Migration with Validation
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Enhanced migration script with comprehensive validation
|
// Enhanced migration function with comprehensive validation
|
||||||
async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
||||||
const result: MigrationResult = {
|
const result: MigrationResult = {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -117,7 +123,7 @@ async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Get current activeDid from settings
|
// 1. Get current activeDid from settings (legacy approach)
|
||||||
const currentSettings = await retrieveSettingsForDefaultAccount();
|
const currentSettings = await retrieveSettingsForDefaultAccount();
|
||||||
const activeDid = currentSettings.activeDid;
|
const activeDid = currentSettings.activeDid;
|
||||||
|
|
||||||
@@ -137,27 +143,20 @@ async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Check if active_identity table already has data
|
// 3. Update active_identity table (new system)
|
||||||
const existingActiveIdentity = await dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingActiveIdentity?.values?.length) {
|
|
||||||
// Update existing record
|
|
||||||
await dbExec(
|
await dbExec(
|
||||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
[activeDid]
|
[activeDid]
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// Insert new record
|
|
||||||
await dbExec(
|
|
||||||
"INSERT INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, datetime('now'))",
|
|
||||||
[activeDid]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.success = true;
|
// 4. Ensure legacy settings.activeDid stays in sync (backward compatibility)
|
||||||
result.dataMigrated = 1;
|
// 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}`);
|
result.warnings.push(`Successfully migrated activeDid: ${activeDid}`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -167,17 +166,9 @@ async function migrateActiveDidToSeparateTable(): Promise<MigrationResult> {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migration result interface
|
|
||||||
interface MigrationResult {
|
|
||||||
success: boolean;
|
|
||||||
errors: string[];
|
|
||||||
warnings: string[];
|
|
||||||
dataMigrated: number;
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 3: Focused API Updates
|
### Phase 3: Focused API Updates with Dual-Write Pattern
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Updated PlatformServiceMixin method - maintains backward compatibility
|
// Updated PlatformServiceMixin method - maintains backward compatibility
|
||||||
@@ -201,93 +192,20 @@ async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New method for active identity management
|
// Enhanced update activeDid method with dual-write pattern
|
||||||
async $getActiveIdentity(): Promise<{ activeDid: string | null }> {
|
|
||||||
try {
|
|
||||||
const result = await this.$dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!result?.values?.length) {
|
|
||||||
return { activeDid: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { activeDid: result.values[0][0] as string };
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("[Settings Trace] ❌ Failed to get active identity:", error);
|
|
||||||
return { activeDid: null };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced method to get settings without activeDid
|
|
||||||
async _getSettingsWithoutActiveDid(): Promise<Settings> {
|
|
||||||
const result = await this.$dbQuery(
|
|
||||||
"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 = ?",
|
|
||||||
[MASTER_SETTINGS_KEY]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!result?.values?.length) {
|
|
||||||
return DEFAULT_SETTINGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._mapColumnsToValues(result.columns, result.values)[0] as Settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced save settings method
|
|
||||||
async $saveSettings(changes: Partial<Settings>): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
// Remove fields that shouldn't be updated
|
|
||||||
const { accountDid, id, activeDid, ...safeChanges } = changes;
|
|
||||||
|
|
||||||
if (Object.keys(safeChanges).length > 0) {
|
|
||||||
// Convert settings for database storage
|
|
||||||
const convertedChanges = this._convertSettingsForStorage(safeChanges);
|
|
||||||
const setParts: string[] = [];
|
|
||||||
const params: unknown[] = [];
|
|
||||||
|
|
||||||
Object.entries(convertedChanges).forEach(([key, value]) => {
|
|
||||||
if (value !== undefined) {
|
|
||||||
setParts.push(`${key} = ?`);
|
|
||||||
params.push(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (setParts.length > 0) {
|
|
||||||
params.push(MASTER_SETTINGS_KEY);
|
|
||||||
await this.$dbExec(
|
|
||||||
`UPDATE settings SET ${setParts.join(", ")} WHERE id = ?`,
|
|
||||||
params,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle activeDid separately in new table
|
|
||||||
if (changes.activeDid !== undefined) {
|
|
||||||
await this.$updateActiveDid(changes.activeDid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("[PlatformServiceMixin] Error saving settings:", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced update activeDid method
|
|
||||||
async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
if (newDid === null) {
|
if (newDid === null) {
|
||||||
// Clear active identity
|
// Clear active identity in both tables
|
||||||
await this.$dbExec(
|
await this.$dbExec(
|
||||||
"UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1"
|
"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 {
|
} else {
|
||||||
// Validate DID exists before setting
|
// Validate DID exists before setting
|
||||||
const accountExists = await this.$dbQuery(
|
const accountExists = await this.$dbQuery(
|
||||||
@@ -300,11 +218,17 @@ async $updateActiveDid(newDid: string | null): Promise<boolean> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update active identity
|
// Update active identity in new table
|
||||||
await this.$dbExec(
|
await this.$dbExec(
|
||||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
[newDid]
|
[newDid]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Keep legacy field in sync (backward compatibility)
|
||||||
|
await this.$dbExec(
|
||||||
|
"UPDATE settings SET activeDid = ? WHERE id = ?",
|
||||||
|
[newDid, MASTER_SETTINGS_KEY]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal tracking
|
// Update internal tracking
|
||||||
@@ -431,30 +355,30 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac
|
|||||||
## What Works (Evidence)
|
## What Works (Evidence)
|
||||||
|
|
||||||
- ✅ **Current activeDid storage** in settings table
|
- ✅ **Current activeDid storage** in settings table
|
||||||
- **Time**: 2025-08-29T07:24Z
|
- **Time**: 2025-08-29T08:03Z
|
||||||
- **Evidence**: `src/db/tables/settings.ts:25` - activeDid field exists
|
- **Evidence**: `src/db-sql/migration.ts:67` - activeDid field exists in initial migration
|
||||||
- **Verify at**: Current database schema and Settings type definition
|
- **Verify at**: Current database schema and Settings type definition
|
||||||
|
|
||||||
- ✅ **PlatformServiceMixin integration** with activeDid
|
- ✅ **PlatformServiceMixin integration** with activeDid
|
||||||
- **Time**: 2025-08-29T07:24Z
|
- **Time**: 2025-08-29T08:03Z
|
||||||
- **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking
|
- **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking
|
||||||
- **Verify at**: Component usage across all platforms
|
- **Verify at**: Component usage across all platforms
|
||||||
|
|
||||||
- ✅ **Database migration infrastructure** exists
|
- ✅ **Database migration infrastructure** exists
|
||||||
- **Time**: 2025-08-29T07:24Z
|
- **Time**: 2025-08-29T08:03Z
|
||||||
- **Evidence**: `src/db-sql/migration.ts:31` - migration system in place
|
- **Evidence**: `src/db-sql/migration.ts:31` - migration system in place
|
||||||
- **Verify at**: Existing migration scripts and database versioning
|
- **Verify at**: Existing migration scripts and database versioning
|
||||||
|
|
||||||
## What Doesn't (Evidence & Hypotheses)
|
## What Doesn't (Evidence & Hypotheses)
|
||||||
|
|
||||||
- ❌ **No separate active_identity table** exists
|
- ❌ **No separate active_identity table** exists
|
||||||
- **Time**: 2025-08-29T07:24Z
|
- **Time**: 2025-08-29T08:03Z
|
||||||
- **Evidence**: Database schema only shows settings table
|
- **Evidence**: Database schema only shows settings table
|
||||||
- **Hypothesis**: Table needs to be created as part of migration
|
- **Hypothesis**: Table needs to be created as part of migration
|
||||||
- **Next probe**: Create migration script for new table
|
- **Next probe**: Add migration to existing MIGRATIONS array
|
||||||
|
|
||||||
- ❌ **Data corruption issues** with orphaned activeDid references
|
- ❌ **Data corruption issues** with orphaned activeDid references
|
||||||
- **Time**: 2025-08-29T07:24Z
|
- **Time**: 2025-08-29T08:03Z
|
||||||
- **Evidence**: `IdentitySwitcherView.vue:175` - `hasCorruptedIdentity` detection
|
- **Evidence**: `IdentitySwitcherView.vue:175` - `hasCorruptedIdentity` detection
|
||||||
- **Hypothesis**: Current schema allows activeDid to point to non-existent accounts
|
- **Hypothesis**: Current schema allows activeDid to point to non-existent accounts
|
||||||
- **Next probe**: Implement foreign key constraints in new table
|
- **Next probe**: Implement foreign key constraints in new table
|
||||||
@@ -522,7 +446,7 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
|
|||||||
|
|
||||||
| Owner | Task | Exit Criteria | Target Date (UTC) |
|
| Owner | Task | Exit Criteria | Target Date (UTC) |
|
||||||
|-------|------|---------------|-------------------|
|
|-------|------|---------------|-------------------|
|
||||||
| Development Team | Create enhanced migration script | Migration script with validation and rollback | 2025-08-30 |
|
| Development Team | Add migration to existing MIGRATIONS array | Migration script integrated with existing system | 2025-08-30 |
|
||||||
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-08-30 |
|
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-08-30 |
|
||||||
| Development Team | Update PlatformServiceMixin | Core methods updated and tested | 2025-08-31 |
|
| Development Team | Update PlatformServiceMixin | Core methods updated and tested | 2025-08-31 |
|
||||||
| Development Team | Implement foreign key constraints | Schema validation prevents corruption | 2025-08-31 |
|
| Development Team | Implement foreign key constraints | Schema validation prevents corruption | 2025-08-31 |
|
||||||
@@ -552,7 +476,7 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
|
|||||||
## Collaboration Hooks
|
## Collaboration Hooks
|
||||||
|
|
||||||
- **Sign-off checklist**:
|
- **Sign-off checklist**:
|
||||||
- [ ] Migration script tested on development database
|
- [ ] Migration script integrated with existing MIGRATIONS array
|
||||||
- [ ] Foreign key constraints implemented and tested
|
- [ ] Foreign key constraints implemented and tested
|
||||||
- [ ] PlatformServiceMixin updated and tested
|
- [ ] PlatformServiceMixin updated and tested
|
||||||
- [ ] Rollback procedures validated
|
- [ ] Rollback procedures validated
|
||||||
@@ -571,31 +495,33 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
|
|||||||
|
|
||||||
## What Needs to Change
|
## What Needs to Change
|
||||||
|
|
||||||
### **1. Database Schema**
|
### **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
|
- Create `active_identity` table with foreign key constraints
|
||||||
- Add performance indexes
|
- Add performance indexes
|
||||||
- Remove `activeDid` field from `settings` table
|
- **Keep `activeDid` field in `settings` table temporarily** for backward compatibility
|
||||||
|
- **Preserve `MASTER_SETTINGS_KEY = "1"`** for legacy migration support
|
||||||
|
|
||||||
### **2. PlatformServiceMixin Methods**
|
### **2. PlatformServiceMixin Methods**
|
||||||
- `$accountSettings()` - integrate with new table
|
- `$accountSettings()` - integrate with new table while maintaining backward compatibility
|
||||||
- `$saveSettings()` - handle activeDid in new table
|
- `$saveSettings()` - handle activeDid in new table, sync with legacy field
|
||||||
- `$updateActiveDid()` - validate and update new table
|
- `$updateActiveDid()` - validate and update new table, sync with legacy field
|
||||||
- `$getActiveIdentity()` - new method for identity management
|
- `$getActiveIdentity()` - new method for identity management
|
||||||
|
|
||||||
### **3. Master Settings Functions**
|
### **3. Master Settings Functions**
|
||||||
- `retrieveSettingsForDefaultAccount()` - integrate with new table
|
- `retrieveSettingsForDefaultAccount()` - integrate with new table while preserving legacy support
|
||||||
- `$getMergedSettings()` - integrate with new table
|
- `$getMergedSettings()` - integrate with new table while preserving legacy support
|
||||||
|
|
||||||
### **4. Migration Scripts**
|
### **4. Type Definitions**
|
||||||
- Create migration script with validation
|
- **Keep `activeDid` in Settings type temporarily** for backward compatibility
|
||||||
- Implement rollback procedures
|
|
||||||
- Add data corruption detection
|
|
||||||
|
|
||||||
### **5. Type Definitions**
|
|
||||||
- Update Settings type to remove activeDid
|
|
||||||
- Create ActiveIdentity type for new table
|
- Create ActiveIdentity type for new table
|
||||||
- Update related interfaces
|
- 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
|
## What Doesn't Need to Change
|
||||||
|
|
||||||
- **All Vue components** - API layer handles migration transparently
|
- **All Vue components** - API layer handles migration transparently
|
||||||
@@ -603,3 +529,48 @@ async function rollbackActiveDidMigration(): Promise<boolean> {
|
|||||||
- **User interface** - No changes to identity selection UI
|
- **User interface** - No changes to identity selection UI
|
||||||
- **Authentication flow** - Existing system unchanged
|
- **Authentication flow** - Existing system unchanged
|
||||||
- **Component logic** - All activeDid handling through API methods
|
- **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
|
||||||
Reference in New Issue
Block a user