| name | contract-based-porting |
| description | Port native Android/iOS code to Flutter (or any target language) using Contract-Based TDD. Guarantees zero missed features by extracting the complete API surface from the reference project first, writing contract tests, then implementing RED→GREEN. Use when porting Kotlin/Swift/Java to Dart, or any cross-language feature parity task. |
Contract-Based Porting: Cross-Language Feature Parity via TDD
When to Use
- Porting native Android (Kotlin/Java) or iOS (Swift/ObjC) features to Flutter (Dart)
- Ensuring a Flutter app has 100% feature parity with a reference native implementation
- Any cross-language porting task where you need guaranteed completeness
- Migrating from one framework to another (e.g., React→Vue, Express→FastAPI)
Core Principle
Never port line-by-line. Port contract-by-contract.
The #1 failure mode in cross-language porting is missing features — not bugs. The fix is to extract the entire public API surface before writing a single line of target code.
3-Step Methodology
Step 1: Extract the Single Source of Truth (SSOT)
Find the one file in the reference project that serves as the command/capability registry. Common patterns:
| Language | Pattern | Example |
|---|
| Kotlin/Android | Registry object | InvokeCommandRegistry.kt |
| Swift/iOS | Protocol constants | ProtocolConstants.swift |
| TypeScript/Node | Route map | routes/index.ts |
| Python/Django | URL patterns | urls.py |
What to extract:
1. All public commands/endpoints/routes → exact string identifiers
2. All capabilities/permissions → exact string identifiers
3. Constructor parameters → each handler's config surface
4. Error codes → all structured error code strings
How to find it:
grep -rn 'register\|addCommand\|mapOf.*command' --include="*.kt" | head -20
grep -rn 'static.*let.*command\|case.*=.*"' --include="*.swift" | head -20
grep -rn '"[a-z]+\.[a-z]+"' --include="*.kt" | sort -u
Step 2: Write Contract Tests (RED Phase)
Create a test file that enumerates every item from the SSOT and asserts the target project advertises/implements them:
// Example: command_registry_contract_test.dart
test('must advertise all 31 reference commands', () {
// SSOT: extracted from InvokeCommandRegistry.kt
const referenceCommands = <String>{
'canvas.open', 'canvas.close', 'canvas.inject',
'screen.record', 'screen.brightness',
'camera.snap', 'camera.clip',
// ... all 31
};
// Target: what the Flutter app actually advertises
final advertised = buildNodeCommands(allPermsGranted).toSet();
// Gap analysis via set difference
final missing = referenceCommands.difference(advertised);
final extra = advertised.difference(referenceCommands);
expect(missing, isEmpty, reason: 'Missing commands: $missing');
expect(extra, isEmpty, reason: 'Extra commands: $extra');
expect(advertised.length, referenceCommands.length);
});
This test MUST fail initially — that's the RED in TDD. The failures tell you exactly what's missing.
Step 3: Implement Until GREEN
For each missing item from the contract test:
- Check if handler exists — often the handler code is already written but not advertised
- If handler exists → just wire it (add to command list, register provider)
- If handler missing → port the reference implementation:
- Read the reference handler (Kotlin/Swift)
- Write the equivalent Dart version
- Focus on the contract (inputs/outputs), not the implementation
flutter test test/protocol/command_registry_contract_test.dart
flutter analyze
Advanced Patterns
Pattern A: UI Wiring Audit
After command parity, audit the integration layer — features implemented but not wired to UI:
grep -rn 'toggleAutoRestart\|seamColorProvider\|TrustPromptDialog' lib/ --include="*.dart" | \
grep -v '.g.dart\|_test.dart'
Pattern B: Error Code Parity
Port the reference error parser for consistent error handling:
// Target: invoke_error_parser.dart
ParsedInvokeError parseInvokeErrorMessage(String raw) { ... }
Pattern C: State/Model Field Parity
Compare state classes field-by-field:
grep -A5 'data class.*State\|val ' ConnectionState.kt
grep -A5 'class.*Snapshot\|final ' gateway_connection_provider.dart
Add missing fields (e.g., seamColorHex, tlsFingerprint) with copyWith support.
Pattern D: Capability Advertising
Capabilities are separate from commands. The reference may advertise capabilities conditionally:
// Always-on
caps.add('canvas');
caps.add('device');
// Permission-gated
if (perms['camera'] ?? false) caps.add('camera');
// Platform-specific
if (Platform.isAndroid) caps.add('foreground_service');
Verification Checklist
After all contract tests pass:
flutter test
flutter analyze
echo "Commands: $(grep -c 'expect.*command' test/protocol/command_registry_contract_test.dart)"
echo "Capabilities: $(grep -c 'expect.*capability' test/protocol/command_registry_contract_test.dart)"
Common Mistakes
- Porting code without porting the contract first → guaranteed to miss features
- Copying implementation instead of contract → fragile, wrong idioms
- Not checking for "implemented but not wired" → handler exists, never called
- Ignoring conditional logic → permission/platform gates differ across languages
- Skipping error code porting → gateway receives unstructured errors
Real-World Results
This methodology was used to port openclaw-node (Kotlin+Swift) → claw_node (Flutter):
- 31/31 commands parity (7 were missing-but-implemented)
- 14/14 capabilities parity
- 5 integration gaps found and fixed (error parser, voice auto-restart, seam color, trust prompt, session drawer)
- 245 tests, 0 analysis issues
- Total time: ~2 sessions with zero manual regression testing
Related skills
See What's inside for related categories — contract-based-porting is a refactoring methodology without natural composition partners in other skill domains.