| name | add-use-case |
| description | Add a new use case interactor with abstraction, implementation, fake, and test. Use when adding new business logic to a feature. |
Add Use Case
Create a new interactor with all four required files.
Parameters: feature name, use case name, type (CenterPostInteractor for one-shot, CenterPostSubjectInteractor for streaming)
Context (optional)
The user may provide additional context in three ways — all are optional:
- Bare — just the feature name, use case name, and type. Scaffold with placeholders.
@file reference — e.g., /add-use-case @specs/submit-order.md. The CLI resolves the file and includes its content. Use it to populate param/result types, repository calls, business logic, fake defaults, and test assertions instead of using placeholders. If no arguments are provided, extract feature name, use case name, and type from the spec's Overview / Use Cases section.
- Inline description — free text typed after the parameters (or on its own). Extract whatever is provided (feature name, use case name, param types, result types, business rules, repository methods) and use it the same way as a spec file.
When context is provided, replace placeholders with real values everywhere: abstract type params, impl logic, fake defaults, and test cases. If context is partial, fill in what you can and leave // TODO only for genuinely unknown parts.
Templates are available in .agents/templates/new-spec.md for structured input.
Pre-flight: Grill the Spec
When the user provides a spec via @file, scan it for unresolved markers before scaffolding (see the grill-me skill for the full marker list): <!-- TODO --> placeholders, empty - [ ] AC items, empty required header fields, raw template placeholder prose, ... table cells, or unconfirmed reverse-spec presumptions (presumably / appears to). If any are present, stop and run /grill-me @{spec} first, then resume this skill.
The conversion skills (/ac-to-spec, /reverse-spec) grill inline before producing output, so a marker-laden spec usually means the spec was hand-authored from a template or has gone stale. Skip the pre-flight only if the user explicitly says "skip the grill" — in that case, surface unresolved markers as // TODO comments in the generated code and call them out in the final summary.
Reference Standards
- DI patterns:
.agents/standards/dependency-injection.md
- CenterPost interactors:
.agents/standards/centerpost.md
Reference
- Abstract:
features/order/api/domain/src/commonMain/.../GetOrderContent.kt
- Impl:
features/order/impl/domain/src/commonMain/.../GetOrderContentImpl.kt
- Fake:
features/order/test/src/commonMain/.../FakeGetOrderContent.kt
- Test:
features/order/impl/domain/src/commonTest/.../GetOrderContentImplTest.kt
Files to Create
1. Abstract Use Case — api/domain/
features/{feature}/api/domain/src/commonMain/kotlin/com/mockdonalds/app/features/{feature}/api/domain/{Name}.kt
package com.mockdonalds.app.features.{feature}.api.domain
import com.mockdonalds.app.core.centerpost.CenterPostSubjectInteractor
abstract class {Name} : CenterPostSubjectInteractor<Unit, {ResultType}>()
For one-shot use cases, use CenterPostInteractor instead:
abstract class {Name} : CenterPostInteractor<{Params}, {ResultType}>()
2. Implementation — impl/domain/
features/{feature}/impl/domain/src/commonMain/kotlin/com/mockdonalds/app/features/{feature}/domain/{Name}Impl.kt
package com.mockdonalds.app.features.{feature}.domain
import com.mockdonalds.app.features.{feature}.api.domain.{Name}
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import kotlinx.coroutines.flow.Flow
@ContributesBinding(AppScope::class)
class {Name}Impl(
private val repository: {Feature}Repository,
) : {Name}() {
override fun createObservable(params: Unit): Flow<{ResultType}> {
return repository.getData()
}
}
3. Fake — test/
features/{feature}/test/src/commonMain/kotlin/com/mockdonalds/app/features/{feature}/test/Fake{Name}.kt
package com.mockdonalds.app.features.{feature}.test
import com.mockdonalds.app.features.{feature}.api.domain.{Name}
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class Fake{Name}(
initial: {ResultType} = DEFAULT,
) : {Name}() {
private val _content = MutableStateFlow(initial)
override fun createObservable(params: Unit): Flow<{ResultType}> = _content
fun emit(content: {ResultType}) {
_content.value = content
}
companion object {
val DEFAULT =
}
}
4. Test — impl/domain/commonTest/
features/{feature}/impl/domain/src/commonTest/kotlin/com/mockdonalds/app/features/{feature}/domain/{Name}ImplTest.kt
package com.mockdonalds.app.features.{feature}.domain
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.flow.first
class {Name}ImplTest : BehaviorSpec({
Given("a {Name}Impl") {
val repository =
val impl = {Name}Impl(repository)
When("observing data") {
Then("it should return expected content") {
val result = impl.createObservable(Unit).first()
}
}
}
})
Key Rules
- Abstract use case in
api/domain/ — public contract
- Impl in
impl/domain/ — private, must have @ContributesBinding(AppScope::class)
- Fake in
test/src/commonMain/ — NOT in commonTest
- Impl must extend the abstract class (Konsist enforces this)
- Every abstract use case must have a matching Impl (Konsist enforces this)
Post-Change Verification — MANDATORY
Work is NEVER complete until verification passes. Run the verify skill to validate all changes. It will:
- Detect which modules were affected by the new use case files
- Run lint, unit tests, and architecture checks scoped to those modules
- Catch naming violations, missing
@ContributesBinding, abstract/impl pairing issues
If ANY check fails, fix the issue and re-run. Do not declare the task complete until verification passes.