بنقرة واحدة
add-interactor
Scaffold a new Strata interactor with interface, implementation, fake, and test
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Scaffold a new Strata interactor with interface, implementation, fake, and test
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Add a new screen to an existing feature module with Store, UI, factories, and tests
Add missing test cases, targeting a specific file or all changed files on the branch
Thorough pre-merge audit — dependency graph, correctness, conventions, security, test coverage, and improvement suggestions
Quick review of the current diff for bugs, logic errors, convention violations, and test gaps
Scaffold a new feature module with the appropriate layers and MESA conventions
Bump library version in VERSION file and prepare release notes
| name | add-interactor |
| description | Scaffold a new Strata interactor with interface, implementation, fake, and test |
| disable-model-invocation | true |
| argument-hint | <feature-name> <InteractorName> [--observe] |
Scaffold a new Strata interactor for an existing feature module, including the implementation, fake, and test.
Input: $ARGUMENTS
Extract:
Summary)FetchUser, ObserveLastSavedValue)StrataInteractor (one-shot async, returns StrataResult<R>)--observe → StrataSubjectInteractor (stream, exposes AsyncStream<T> via .stream)Verify the feature module exists at <AppTarget>/<FeatureName>/. If it doesn't exist, stop and ask the user whether they intended a different feature name or whether they need to create the feature first with /add-feature. Do not proceed until the feature exists.
Ask the user:
Int, String, Void)User, [Item], Int?)Read the feature module's existing files to understand:
Domain/Place at: <AppTarget>/<FeatureName>/Domain/<InteractorName>UseCase.swift
StrataInteractor (one-shot):
import Foundation
import Strata
public final class <InteractorName>UseCase: StrataInteractor<P, R> {
private let repository: <FeatureName>Repository
public init(repository: <FeatureName>Repository) {
self.repository = repository
super.init()
}
public override func doWork(params: P) async -> StrataResult<R> {
return await executeCatching(params: params) { p in
try await repository.someMethod(p)
}
}
}
StrataSubjectInteractor (observe):
import Foundation
import Strata
public class <InteractorName>UseCase: StrataSubjectInteractor<P, T> {
private let repository: <FeatureName>Repository
public init(repository: <FeatureName>Repository) {
self.repository = repository
super.init()
}
public override func createObservable(params: P) -> AsyncStream<T> {
return repository.observe()
}
}
Place in the consuming module's test directory under a Fakes/ subdirectory (e.g., <AppTarget>Tests/Fakes/Fake<InteractorName>UseCase.swift). Typically this is the app's test target.
StrataInteractor Fake:
import Foundation
import Strata
@testable import <AppTarget>
final class Fake<InteractorName>UseCase: StrataInteractor<P, R>, @unchecked Sendable {
var stubResult: StrataResult<R> = .success(/* default value */)
override func doWork(params: P) async -> StrataResult<R> {
return stubResult
}
}
StrataSubjectInteractor Fake:
import Foundation
import Strata
@testable import <AppTarget>
final class Fake<InteractorName>UseCase: StrataSubjectInteractor<P, T>, @unchecked Sendable {
var values: [T] = []
override func createObservable(params: P) -> AsyncStream<T> {
AsyncStream { continuation in
for value in values {
continuation.yield(value)
}
continuation.finish()
}
}
}
Place at: <AppTarget>Tests/<InteractorName>UseCaseTests.swift
For MESA library tests (Swift Testing):
import Foundation
import Testing
@testable import <AppTarget>
@Suite("<InteractorName>UseCase")
struct <InteractorName>UseCaseTests {
@Test("executes successfully with valid params")
func executeSuccess() async {
// Given
let useCase = <InteractorName>UseCase(repository: FakeRepository())
// When
let result = await useCase.execute(params: /* test params */)
// Then
#expect(result.getOrNull() == /* expected value */)
}
@Test("returns failure on repository error")
func executeFailure() async {
// Given
let useCase = <InteractorName>UseCase(repository: FailingFakeRepository())
// When
let result = await useCase.execute(params: /* test params */)
// Then
#expect(result.getOrNull() == nil)
}
}
For Counter app tests (XCTest):
import XCTest
@testable import Counter
final class <InteractorName>UseCaseTests: XCTestCase {
func test_execute_succeeds() async {
// Given
let useCase = <InteractorName>UseCase(repository: FakeRepository())
// When
let result = await useCase.execute(params: /* test params */)
// Then
XCTAssertEqual(result.getOrNull(), /* expected value */)
}
}
For StrataSubjectInteractor, test the stream output:
@Test("emits values from createObservable")
func basicEmission() async {
let useCase = <InteractorName>UseCase(repository: FakeRepository())
let task = Task { () -> [T] in
var collected: [T] = []
for await value in useCase.stream {
collected.append(value)
if collected.count >= 1 { break }
}
return collected
}
try? await Task.sleep(nanoseconds: 10_000_000)
useCase(/* params */)
let collected = await task.value
#expect(collected == [/* expected */])
}
All generated files MUST include the Apache 2.0 license header:
/*
* Copyright 2026 Jason Jamieson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Run cd MESA && swift test --parallel for the affected modules to verify compilation.
Report: