-
Notifications
You must be signed in to change notification settings - Fork 0
Integration Agent
Completes the 4 integration points and generates the living specification. Invoked by creating-kmp-feature skill in Phase 4 (Implementation), after data + UI agents finish.
Model: sonnet · Color: green · Allowed tools: Read, Write, Edit, Glob, Grep, ./gradlew
| # | Point | File | Pattern |
|---|---|---|---|
| 1 | Gradle Include | settings.gradle.kts |
include(":feature:{featurename}") |
| 2 | Gradle Dependency | composeApp/build.gradle.kts |
implementation(project(":feature:{featurename}")) |
| 3 | DI Init | {INIT_KOIN_PATH} |
{Feature}Modules.initialize() |
| 4 | Navigation | {NAV_HOST_PATH} |
{featurename}(onBackClick = {...}) |
- Create DI module (
di/{Feature}Modules.kt). - Integration Point 1 — Gradle Include.
- Integration Point 2 — Gradle Dependency.
- Integration Point 3 — DI Initialization.
- Integration Point 4 — Navigation (reads Screen for callback signatures).
- Validate:
./gradlew assembleDebug && ./gradlew ktlintFormat. - Generate
spec.mdusing the template from_shared/spec-template.md.
The DI module is an object extending BaseFeature. This is the only correct pattern.
package {PKG_PREFIX}.{featurename}.di
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.bind
import org.koin.dsl.module
import {CORE_COMMON_PKG}.di.base.BaseFeature
// (datasource + repository + viewmodel imports)
object DashboardModules : BaseFeature(DashboardModules::class.simpleName.toString()) {
override fun getKoinModules(): List<Module> =
listOf(
module {
// Data layer
singleOf(::DashboardLocalDataSourceImpl).bind<DashboardLocalDataSource>()
singleOf(::DashboardRemoteDataSourceImpl).bind<DashboardRemoteDataSource>()
singleOf(::DashboardRepositoryImpl).bind<DashboardRepository>()
// Presentation layer
viewModelOf(::DashboardViewModel)
},
)
override fun initialize() {
DashboardModules
}
}The feature's nav extension is invoked by BaseAppNavHost.kt. The host never writes a composable<Route> { ... } block for the feature.
// composeApp/.../BaseAppNavHost.kt
@Composable
fun BaseAppNavHost(modifier: Modifier) {
val navController = rememberNavController()
XNavHost(
modifier = modifier,
navController = navController,
startDestination = DashboardRoute,
) {
dashboard(
onActionClick = { actionId ->
if (actionId == "send") navController.navigate(SendRoute)
if (actionId == "receive") navController.navigate(ReceiveRoute)
},
onBackToDashboard = {
navController.popBackStack(DashboardRoute, inclusive = false)
},
)
send(onBackClick = { navController.popBackStack() })
receive(onBackClick = { navController.popBackStack() })
}
}initKoin.kt registers every feature module's .initialize():
private fun initializeFeatures() {
CommonModules.initialize()
DataModules.initialize()
DashboardModules.initialize()
SendModules.initialize()
ReceiveModules.initialize()
}
fun initKoin(appDeclaration: KoinAppDeclaration = {}): KoinApplication {
initializeFeatures()
return startKoin {
appDeclaration()
modules(getAllModules()) // FeatureRegistry collects them
}
}The agent appends {Feature}Modules.initialize() to the existing initializeFeatures() block.
Critical: Copy WHY content from the PRD before Phase 4 deletes it:
- Goals
- Non-Goals
- Background & Rationale
- Design Decisions
Template lives at .claude/skills/_shared/spec-template.md.
## Integration Complete: {featurename}
### Files Created/Modified
- di/{Feature}Modules.kt (created)
- settings.gradle.kts (modified)
- composeApp/build.gradle.kts (modified)
- {INIT_KOIN_PATH} (modified)
- {NAV_HOST_PATH} (modified)
### Integration Points
✅ 1. Gradle Include
✅ 2. Gradle Dependency
✅ 3. DI Initialization
✅ 4. Navigation Wiring
### Validation
✅ ./gradlew assembleDebug
✅ ./gradlew ktlintFormat
### Spec Generated
✅ .claude/docs/{featurename}/spec.md
Next: navController.navigate({Feature}Route)
Back to Feature-Development-Agents | Agents