Agent: Anthropic Claude (Sonnet, Opus, Haiku) Created: 2025-11-09 Last Updated: January 2025 Status: iOS-Specific Features Complete ✅
- PROTOCOL.md (this directory) - Cross-platform v1 protocol specification
⚠️ SOURCE OF TRUTH: If accessible,~/WebstormProjects/redo-web-app/PROTOCOL.mdsupersedes this copy- The web app is the leader platform and protocol authority
- AI.md (this directory) - Universal AI agent instructions (shared across all AI agents)
- This file (CLAUDE.md) - Claude-specific workflows and patterns
- PLANNING.md - Architecture decisions and rationale
- SESSION_X_SUMMARY.md - Recent development progress
Redo iOS is a native iOS task management application implementing the proven v1 event sourcing protocol shared with:
- Web App (leader platform): TypeScript/React at
~/WebstormProjects/redo-web-app - Android App: Kotlin/Jetpack Compose at
~/StudioProjects/redo-android - Kotlin CLI: Core models at
~/IdeaProjects/redo
Event Sourcing + Local-First + Cross-Platform Sync
↓
Immutable Change Log (Git-like) → State Reconstruction → Real-time UI
↓
Local Storage (Primary) ↔ Firebase (Optional Sync)
ZERO tolerance for invalid nodes. This is non-negotiable for cross-platform compatibility.
Requirements:
versionMUST be exactly 1idMUST be "sha256:" + 64 lowercase hex charsparentsMUST be array of valid change IDstimestamp.lamportMUST be > 0timestamp.wallMUST be valid ISO 8601author.userIdMUST be 32 lowercase hex charsauthor.publicKey(if present) MUST be 64 lowercase hex charssignature(if present) MUST be 128 lowercase hex chars- NO Base58 encoding (web app's early mistake)
- NO uppercase hex (breaks cross-platform hashing)
Validation Location: RedoCore/Services/ChangeLogValidator.swift
AI Agent Instruction: If you ever consider relaxing these rules, STOP and review PLANNING.md section 2.2. The web app spent 50+ hours debugging issues from lax validation.
All change IDs are SHA-256 hashes of canonical JSON:
- Keys sorted alphabetically
- No whitespace
- RFC 8785 compliant
- Deterministic across platforms
Implementation: RedoCrypto/ContentAddressing.swift
AI Agent Instruction: Any changes to models MUST preserve canonical JSON compatibility. Test against web/Android hash outputs.
All operations MUST be instant (like Git):
getAllTasks()=git log(reads local, instant)createTask()=git commit(writes local, instant)syncChanges()=git fetch/push(background, async)
AI Agent Instruction: Never block UI on network operations. If you add Firebase calls in the UI layer, you're doing it wrong.
Current state is NEVER cached, always reconstructed from change log:
- Load all changes from storage
- Sort by Lamport clock (causal ordering)
- Validate each change (strict v1)
- Replay actions sequentially
- Return reconstructed tasks
Implementation: RedoCore/Services/StateReconstructor.swift
AI Agent Instruction: If you find yourself caching task state in UserDefaults/CoreData, you've misunderstood the architecture. The change log is the single source of truth.
redo-ios/
├── Sources/
│ ├── RedoCore/ # Business logic (platform-agnostic)
│ │ ├── Models/ # RedoTask, TodoTask, ChangeLogEntry
│ │ ├── Services/ # StateReconstructor, ChangeLogValidator
│ │ └── Storage/ # ChangeLogStorage, KeychainService
│ │
│ ├── RedoCrypto/ # Cryptography (Ed25519, SHA-256, CanonicalJSON)
│ │ ├── Ed25519Manager.swift
│ │ ├── ContentAddressing.swift
│ │ └── CanonicalJSON.swift
│ │
│ └── RedoUI/ # SwiftUI interface
│ ├── Views/ # TaskListView, CreateTaskView
│ ├── ViewModels/ # AppViewModel
│ ├── Components/ # MatrixTaskCard, etc.
│ ├── Theme/ # MatrixTheme
│ └── Sync/ # FirebaseSyncService
│
├── Tests/
│ ├── RedoCoreTests/ # Business logic tests
│ └── RedoCryptoTests/ # Cryptography tests
│
└── Docs/
├── PLANNING.md # Comprehensive architecture document
├── SETUP.md # Setup instructions
└── CLAUDE.md # This file
- Core Models - RedoTask, TodoTask, ChangeLogEntry with full business logic
- Cryptography - Ed25519 signing, SHA-256 hashing, canonical JSON
- State Reconstruction - Event replay engine with validation
- Local Storage - File-based change log storage + Keychain for keys
- Firebase Sync - Cloud sync service (matches web/Android architecture)
- SwiftUI UI - Matrix-themed TaskListView, CreateTaskView, task cards
- View Models - AppViewModel with MVVM pattern
- Test Foundation - Unit tests for validation and cryptography
-
Testing:
- StateReconstructor tests (port from Android's 18 tests)
- Storage tests (file operations, deduplication)
- Cross-platform hash verification
- UI tests (SwiftUI previews → UI automation)
-
Additional Views:
- TaskDetailView (full task info, history, TODOs)
- SettingsView (export/import, sync toggle, identity info)
- HistoryView (DAG visualization like web app)
-
Firebase Integration:
- Google OAuth authentication
- Real-time sync listener
- Offline/online status indicator
-
Polish:
- Animations and transitions
- Haptic feedback
- Accessibility (VoiceOver, Dynamic Type)
- Error handling UI
-
Feature Parity:
- Calendar view (matching web/Android)
- Analytics dashboard
- Advanced filtering and search
- Task snoozing
-
iOS-Specific:
- Widget (home screen task summary)
- Live Activities (task completion tracking)
- Shortcuts integration
- ShareSheet for task export
-
Performance:
- Change log pagination (for 1000s of tasks)
- Background sync optimization
- Memory profiling
-
Build Version Enforcement ✅
- iOS: Use Xcode build phases to auto-increment CFBundleVersion
- Prevents re-deployment waste
-
Token Separation ✅
- Google OAuth token (for Google APIs) separate from Firebase token
- Store in separate Keychain entries (not same key)
- Web had 50-min bug from mixing these
-
Hex Encoding Only ✅
- All crypto fields lowercase hex (not Base58/Base64)
- Web had 125 Base58 nodes causing CLI sync failures
-
Batched Firestore Operations ✅
- Implemented from day one (web is planning this)
- Fetch in chunks of 10 (Firebase 'in' query limit)
-
OAuth ID for Storage ✅
- Use Google OAuth subject ID for Firebase paths
- Crypto userId for node signing
- Android spent Nov 2025 fixing this mismatch
-
Visual Polish ✅
- iOS gets neon glow from day one (Android missing this)
- Used .shadow() modifiers with multiple layers
- SF Symbols for consistent iconography
-
Comprehensive Tests ✅
- Ported validation test patterns from Android's 65 tests
- Cross-platform compatibility tests included
Before Making Changes:
- Read PLANNING.md (48KB architecture doc)
- Review relevant web/Android implementation
- Check if change affects protocol compatibility
- Write tests FIRST (TDD approach)
Critical Checks:
- Does this change modify canonical JSON serialization? → Test cross-platform hashing
- Does this change validation rules? → Verify against PROTOCOL.md
- Does this block UI on network? → Refactor to background
- Does this cache state? → Use event sourcing instead
When Stuck:
- Reference web app:
~/WebstormProjects/redo-web-app/src/models/RedoNode.ts(1,738 lines) - Reference Android:
~/StudioProjects/redo-android/app/src/main/java/vision/salient/redo/ - Reference protocol:
~/WebstormProjects/redo-web-app/PROTOCOL.md
Setup (see SETUP.md for details):
cd ~/ios_code/redo-ios
swift build # Build all modules
swift test # Run testsCommon Tasks:
- Add new action type: Update ChangeAction enum, add handler in StateReconstructor
- Add new UI: Follow MatrixTheme.swift patterns, use .matrixGradientBackground(), .neonGlow()
- Debug sync: Check ChangeLogStorage → Firebase paths match web/Android exactly
- Add tests: See Tests/ for examples, port patterns from Android test suite
Testing Against Web/Android:
- Generate keypair in iOS
- Copy public key to web app localStorage
- Create task in iOS
- Verify it appears in web app after sync
- Create task in web app
- Verify it appears in iOS after sync
CRITICAL: Must match web/Android exactly
nodes/ # Global collection (CURRENT)
{nodeId}/
id: "sha256:..."
version: 1
parents: ["sha256:..."]
timestamp: { lamport: 5, wall: "2025-11-09T..." }
author: {
userId: "abc123...", # Crypto userId (first 32 of public key)
deviceId: "...",
publicKey: "abc123..." # Full Ed25519 public key (64 hex)
}
action: "CREATE"
taskId: "uuid"
data: { fields: {...} }
signature: "..."
accessList: ["105903..."] # Google OAuth IDs
createdAt: Timestamp
users/
{googleOAuthId}/ # Google OAuth subject ID (NOT crypto userId)
ownedNodes: ["sha256:...", ...] # Array of node IDs
Evolution:
- ❌ PURGED:
users/{oauthId}/changes/{nodeId}(old web architecture) - ❌ ABANDONED:
users/{cryptoUserId}/ownedNodes[](intermediate Android) - ✅ CURRENT:
nodes/{nodeId}+users/{googleOAuthId}/ownedNodes[]
Rationale: Global nodes collection enables future shared tasks feature. Access control via accessList array.
- ✅ Ed25519 key generation, signing, verification
- ✅ Change log validation (v1 protocol)
- ⏳ State reconstruction (TODO: port Android's 18 tests)
- ⏳ Storage operations (TODO: file I/O, deduplication)
- Cross-platform hash consistency (iOS ↔ Kotlin ↔ TypeScript)
- Firebase sync round-trip (iOS → Firebase → iOS)
- Multi-device sync (iOS → Firebase → Android/Web)
- Task creation flow
- Task completion flow
- Filter/search functionality
- Offline mode resilience
Test Vectors (from web app):
// Same ChangeLogEntry should produce same hash
let entry = ChangeLogEntry(/* ... */)
let iosHash = try ContentAddressing.calculateChangeId(entry: entry)
let expectedHash = "sha256:abc123..." // From web/Android
XCTAssertEqual(iosHash, expectedHash)Ed25519 Compatibility:
// Sign in iOS, verify in web/Android
let message = "test"
let signature = try Ed25519Manager.sign(string: message, privateKeyHex: privateKey)
// Paste signature into web app console:
// > Ed25519.verify(publicKey, signature, message)
// Should return true- Task list render: < 16ms (60 FPS)
- State reconstruction: < 100ms (1000 tasks)
- Local operations: < 50ms (create/update/complete)
- Sync: < 500ms (background, non-blocking)
- Change log: ~1KB per task (1000 tasks = ~1MB)
- Reconstructed state: ~500B per task (1000 tasks = ~500KB)
- Total memory: < 10MB for typical usage
- Change log file: ~1MB per 1000 tasks
- iOS limit: Unlimited (stored in Documents directory)
- Cleanup strategy: Archive old changes after 1 year
- Ed25519 for signatures (32-byte keys)
- SHA-256 for content addressing
- All crypto via Apple CryptoKit (hardware-accelerated)
- Private keys in Keychain (kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
- Public keys in Keychain (for backup)
- No keys in UserDefaults/plist files
- Change log stored locally (encrypted at rest by iOS)
- Firebase data encrypted in transit (TLS)
- Firebase data encrypted at rest (Google Cloud default)
- Optional: End-to-end encryption (future feature)
- Firebase security rules enforce access via
accessListarray - Only authenticated users can read/write their nodes
- Multi-user collaboration (future) via shared
accessList
-
Caching Task State
// ❌ WRONG @AppStorage("cachedTasks") var tasks: Data
Change log is the source of truth. Always reconstruct.
-
Blocking UI on Network
// ❌ WRONG func loadTasks() async { tasks = await firebaseSync.getTasks() // Blocks UI! }
Read from local storage first. Sync in background.
-
Mixing Token Types
// ❌ WRONG keychain.save(firebaseToken, forKey: "googleAccessToken")
Google OAuth token ≠ Firebase custom token. Separate keys.
-
Relaxing Validation
// ❌ WRONG if version == 1 || version == 2 { ... } // NO legacy support!
Only v1 protocol accepted. Period.
-
Uppercase Hex
// ❌ WRONG let hash = data.hexEncodedString(uppercase: true)
v1 protocol requires lowercase. No exceptions.
-
Always Reconstruct State
func loadTasks() async throws { let changes = try storage.getAllChanges(userId: userId) tasks = try stateReconstructor.reconstructTasks(from: changes) Task { await syncChanges() } // Background }
-
Validate at Boundaries
func saveChange(_ change: ChangeLogEntry) throws { guard validator.isValidV1Node(change) else { throw ValidationError.invalidNode } try storage.saveChanges(userId: userId, newChanges: [change]) }
-
Test Cross-Platform
func testHashCompatibility() throws { let entry = /* ... */ let iosHash = try ContentAddressing.calculateChangeId(entry: entry) let kotlinHash = "sha256:..." // From Kotlin test XCTAssertEqual(iosHash, kotlinHash) }
Core Business Logic:
Sources/RedoCore/Models/RedoTask.swift- Task template modelSources/RedoCore/Models/TodoTask.swift- Task instance modelSources/RedoCore/Models/ChangeLogEntry.swift- Event sourcing nodeSources/RedoCore/Services/StateReconstructor.swift- Event replay engineSources/RedoCore/Services/ChangeLogValidator.swift- v1 protocol validation
Cryptography:
Sources/RedoCrypto/Ed25519Manager.swift- Signing and verificationSources/RedoCrypto/ContentAddressing.swift- SHA-256 hashingSources/RedoCrypto/CanonicalJSON.swift- RFC 8785 serialization
Storage:
Sources/RedoCore/Storage/ChangeLogStorage.swift- File-based persistenceSources/RedoCore/Storage/KeychainService.swift- Secure key storage
UI:
Sources/RedoUI/Views/TaskListView.swift- Main task listSources/RedoUI/Views/CreateTaskView.swift- Task creationSources/RedoUI/Components/MatrixTaskCard.swift- Task card componentSources/RedoUI/Theme/MatrixTheme.swift- Color palette, typography, modifiersSources/RedoUI/ViewModels/AppViewModel.swift- Main view model
Sync:
Sources/RedoUI/Sync/FirebaseSyncService.swift- Firebase integration
Tests:
Tests/RedoCoreTests/ChangeLogValidatorTests.swift- Validation testsTests/RedoCryptoTests/Ed25519Tests.swift- Cryptography tests
Documentation:
PLANNING.md- Comprehensive architecture (48KB)README.md- Quick start and overviewCLAUDE.md- This file (AI agent context)SETUP.md- Setup instructions
Cross-Platform Protocol:
- Web:
~/WebstormProjects/redo-web-app/PROTOCOL.md(1,603 lines) - Web:
~/WebstormProjects/redo-web-app/ARCHITECTURE.md(1,363 lines)
Reference Implementations:
- Web (leader):
~/WebstormProjects/redo-web-app/src/models/RedoNode.ts - Android:
~/StudioProjects/redo-android/app/src/main/java/vision/salient/redo/model/ChangeLog.kt - Kotlin CLI:
~/IdeaProjects/redo/core/src/main/kotlin/vision/salient/redo/model/ChangeLog.kt
Test Suites:
- Web:
~/WebstormProjects/redo-web-app/src/__tests__/ - Android:
~/StudioProjects/redo-android/app/src/test/java/vision/salient/redo/
-
v0.1.0 (2025-11-09): Foundation complete
- Core models, cryptography, state reconstruction
- Local storage, Firebase sync skeleton
- SwiftUI UI with Matrix theme
- Test foundation
-
v0.2.0 (Planned): Feature parity
- All CRUD operations
- Firebase OAuth integration
- Task detail view, settings, history
- Complete test coverage
-
v1.0.0 (Planned): App Store launch
- Cross-platform sync verified
- Performance optimized
- Accessibility complete
- Production Firebase project
For AI Agents: If uncertain, check:
- PLANNING.md sections 2-7 (architecture decisions)
- Web app's ARCHITECTURE.md (design rationale)
- Android's CROSS_PLATFORM_SYNC_FIXED.md (sync lessons)
For Developers: See SETUP.md for getting started.
End of CLAUDE.md