@mrciphersmith/keryx 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cli.js +125 -51
  2. package/package.json +1 -1
  3. package/src/gdskills/bundled/install-manifest.json +231 -2
  4. package/src/gdskills/bundled/stacks/csharp-dotnet/agent-refs.json +4 -0
  5. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/eval.json +1881 -0
  6. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/scout.json +33 -0
  7. package/src/gdskills/bundled/stacks/csharp-dotnet/pack.json +38 -0
  8. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/coding-style.mdc +100 -0
  9. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/patterns.mdc +107 -0
  10. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/security.mdc +86 -0
  11. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/testing.mdc +89 -0
  12. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/SKILL.md +143 -0
  13. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/evals.json +77 -0
  14. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/SKILL.md +121 -0
  15. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/evals.json +77 -0
  16. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/SKILL.md +134 -0
  17. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/evals.json +76 -0
  18. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/SKILL.md +130 -0
  19. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/evals.json +77 -0
  20. package/src/gdskills/bundled/stacks/flutter-dart/agent-refs.json +4 -0
  21. package/src/gdskills/bundled/stacks/flutter-dart/governance/eval.json +1849 -0
  22. package/src/gdskills/bundled/stacks/flutter-dart/governance/scout.json +33 -0
  23. package/src/gdskills/bundled/stacks/flutter-dart/pack.json +41 -0
  24. package/src/gdskills/bundled/stacks/flutter-dart/rules/coding-style.mdc +98 -0
  25. package/src/gdskills/bundled/stacks/flutter-dart/rules/patterns.mdc +88 -0
  26. package/src/gdskills/bundled/stacks/flutter-dart/rules/security.mdc +91 -0
  27. package/src/gdskills/bundled/stacks/flutter-dart/rules/testing.mdc +101 -0
  28. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/SKILL.md +134 -0
  29. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/evals.json +79 -0
  30. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/SKILL.md +124 -0
  31. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/evals.json +74 -0
  32. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/SKILL.md +139 -0
  33. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/evals.json +77 -0
  34. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/SKILL.md +134 -0
  35. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/evals.json +74 -0
  36. package/src/gdskills/bundled/stacks/kotlin-android/agent-refs.json +4 -0
  37. package/src/gdskills/bundled/stacks/kotlin-android/governance/eval.json +1889 -0
  38. package/src/gdskills/bundled/stacks/kotlin-android/governance/scout.json +34 -0
  39. package/src/gdskills/bundled/stacks/kotlin-android/pack.json +38 -0
  40. package/src/gdskills/bundled/stacks/kotlin-android/rules/coding-style.mdc +89 -0
  41. package/src/gdskills/bundled/stacks/kotlin-android/rules/patterns.mdc +96 -0
  42. package/src/gdskills/bundled/stacks/kotlin-android/rules/security.mdc +90 -0
  43. package/src/gdskills/bundled/stacks/kotlin-android/rules/testing.mdc +89 -0
  44. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/SKILL.md +150 -0
  45. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/evals.json +77 -0
  46. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/SKILL.md +151 -0
  47. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/evals.json +76 -0
  48. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/SKILL.md +139 -0
  49. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/evals.json +78 -0
  50. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/SKILL.md +131 -0
  51. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/evals.json +77 -0
  52. package/src/gdskills/bundled/stacks/swift-ios/agent-refs.json +4 -0
  53. package/src/gdskills/bundled/stacks/swift-ios/governance/eval.json +1803 -0
  54. package/src/gdskills/bundled/stacks/swift-ios/governance/scout.json +32 -0
  55. package/src/gdskills/bundled/stacks/swift-ios/pack.json +38 -0
  56. package/src/gdskills/bundled/stacks/swift-ios/rules/coding-style.mdc +92 -0
  57. package/src/gdskills/bundled/stacks/swift-ios/rules/patterns.mdc +112 -0
  58. package/src/gdskills/bundled/stacks/swift-ios/rules/security.mdc +78 -0
  59. package/src/gdskills/bundled/stacks/swift-ios/rules/testing.mdc +90 -0
  60. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/SKILL.md +144 -0
  61. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/evals.json +75 -0
  62. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/SKILL.md +122 -0
  63. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/evals.json +75 -0
  64. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/SKILL.md +131 -0
  65. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/evals.json +75 -0
  66. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/SKILL.md +149 -0
  67. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/evals.json +76 -0
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: kotlin-android-testing
3
+ description: "Use when a Kotlin/Android module's test suite needs writing, extending, or fixing -- runTest/TestDispatcher for suspend functions and ViewModels, ComposeTestRule semantics-based finders for UI tests, and MockK at the repository/data-source interface boundary."
4
+ triggers:
5
+ - "write tests for this ViewModel"
6
+ - "add a Compose UI test for this screen"
7
+ - "fix this flaky Android test"
8
+ - "test this suspend function"
9
+ - "mock the repository for this unit test"
10
+ - "cover this feature module with tests"
11
+ - "the test for this screen keeps timing out"
12
+ metadata:
13
+ origin: authored
14
+ category: test
15
+ version: "1.0.0"
16
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
17
+ license: "MIT"
18
+ ---
19
+
20
+ # Kotlin/Android testing
21
+
22
+ Write, extend, or fix a Kotlin/Android module's test suite: coroutine
23
+ tests with `runTest`/`TestDispatcher`, Compose UI tests with
24
+ `ComposeTestRule`, and interface-boundary mocking. `rules/testing.mdc`
25
+ carries the full rule set this skill's checklist is built from — read it,
26
+ not just this summary, before writing tests.
27
+
28
+ ## Workflow
29
+
30
+ ### Step 1: Discover the project's test conventions
31
+
32
+ 1. Read the module's `build.gradle(.kts)` test dependencies: MockK vs
33
+ Mockito, `kotlinx-coroutines-test` version, and whether a Compose UI
34
+ test dependency (`androidx.compose.ui:ui-test-junit4`) is present.
35
+ 2. Find the layout: unit tests under `src/test/`, instrumented/Compose UI
36
+ tests under `src/androidTest/`. Match whichever the module already
37
+ uses; do not invent a third location.
38
+ 3. Read 1-2 neighboring test files for: dispatcher-rule setup (a shared
39
+ `MainDispatcherRule` or manual `Dispatchers.setMain`/`resetMain`),
40
+ whether MockK or Mockito is standardized, and existing fixture/builder
41
+ helpers for constructing test data.
42
+
43
+ ### Step 2: Plan test cases
44
+
45
+ **`ViewModel`/suspend functions:** happy path, error/exception path from
46
+ a mocked dependency, and any intermediate state a caller can observe (a
47
+ loading state before a result arrives) using a `StandardTestDispatcher`
48
+ you advance explicitly.
49
+
50
+ **Compose UI:** initial render state, a user interaction
51
+ (`performClick()`, `performTextInput()`) and its resulting state/text
52
+ change, and a conditionally-shown element's visibility.
53
+
54
+ **Repository/data layer:** success and failure responses from the mocked
55
+ network/database boundary, and that a domain-level error type is what
56
+ actually reaches the caller (not a raw exception leaking through).
57
+
58
+ ### Step 3: Write
59
+
60
+ 1. Wrap coroutine test bodies in `runTest { }`; install a `TestDispatcher`
61
+ for `Dispatchers.Main` via a shared JUnit rule (or manual
62
+ `Dispatchers.setMain(...)`/`Dispatchers.resetMain()` in
63
+ `@Before`/`@After`) so `viewModelScope` resolves to it.
64
+ 2. Mock the `Repository`/`ApiService`/data-source interface the class
65
+ under test depends on with MockK (`mockk<Repo>()`,
66
+ `coEvery { repo.fetch() } returns result`) — never stub the class
67
+ under test's own internals.
68
+ 3. For a Compose UI test, use `createComposeRule()` (or
69
+ `createAndroidComposeRule<Activity>()` when an Activity host is
70
+ needed) and find nodes via `onNodeWithText`/
71
+ `onNodeWithContentDescription`/`onNodeWithTag`, not structural
72
+ indexing.
73
+ 4. Advance the test dispatcher deliberately
74
+ (`advanceUntilIdle()`/`runCurrent()`) at the point where the test
75
+ needs to observe an intermediate or final state — never a fixed
76
+ `delay`/`Thread.sleep` to "give it time."
77
+ 5. Assert both the rendered/returned state and, where relevant, a
78
+ `coVerify { repo.save(...) }` that the right interaction actually
79
+ happened.
80
+
81
+ ### Step 4: Run and fix
82
+
83
+ ```bash
84
+ ./gradlew testDebugUnitTest
85
+ ```
86
+
87
+ Run `./gradlew connectedDebugAndroidTest` (or the project's Compose UI
88
+ test task) for instrumented tests when relevant. Fix failing tests (max 3
89
+ iterations) — fix the test, not the source under test, unless the test
90
+ itself has correctly caught a real bug (say so in the report rather than
91
+ silently changing production code).
92
+
93
+ ### Step 5: Report
94
+
95
+ ```
96
+ Generated: feature/profile/ProfileViewModelTest.kt
97
+ - 6 cases (runTest + StandardTestDispatcher), repository mocked with MockK
98
+ - ./gradlew testDebugUnitTest passes
99
+ ```
100
+
101
+ ## Rules
102
+
103
+ - ALWAYS match the project's existing dispatcher-rule and mocking-library
104
+ conventions found in Step 1, not a different project's style.
105
+ - NEVER modify source code — only test files.
106
+ - NEVER use `Thread.sleep`/a fixed `delay` to wait for a coroutine or
107
+ Compose UI update; use `runTest`'s virtual time, `advanceUntilIdle()`,
108
+ or `composeTestRule.waitUntil { ... }`.
109
+ - Mock at the repository/data-source interface boundary, never the class
110
+ under test's own private methods or fields.
111
+
112
+ ## Red Flags
113
+
114
+ | Rationalization | Why it is wrong |
115
+ |---|---|
116
+ | "I'll add a short delay so the coroutine has time to finish" | Non-deterministic and slow; `runTest`'s virtual clock plus `advanceUntilIdle()` makes the wait both instant and deterministic |
117
+ | "I'll just mock a private method on the ViewModel itself to control what it returns" | Testing a mocked-out internal instead of real behavior; mock the repository/data-source interface the ViewModel actually depends on |
118
+ | "This UI test keeps flaking, I'll add a `Thread.sleep(500)` before the assertion" | Hides a real synchronization gap; use `composeTestRule.waitUntil { ... }` or advance the test dispatcher instead |
119
+ | "I'll loosen this assertion to just check the call didn't throw" | Covers nothing about which state or value resulted; assert the actual state/text/interaction outcome |
120
+
121
+ ## Verification
122
+
123
+ Do not report the work done until all of the following hold:
124
+
125
+ - The test file sits at the project's own convention path
126
+ (`src/test/`/`src/androidTest/`), matching Step 1's findings.
127
+ - `./gradlew testDebugUnitTest` (and the instrumented task, if touched)
128
+ exits 0 with every generated test passing.
129
+ - `git status` shows only test files added or modified; no source file
130
+ under test changed.
131
+ - No test synchronizes with `Thread.sleep`/a fixed `delay`.
@@ -0,0 +1,77 @@
1
+ {
2
+ "triggers": {
3
+ "positive": [
4
+ "This ViewModel's loadProfile function needs test coverage before we merge",
5
+ "Add a test that clicking the retry button actually triggers a refetch",
6
+ "The repository call in this suspend function isn't covered by any test",
7
+ "Write a test for the error state shown when the network call fails",
8
+ "This screen's test keeps waiting forever, can you fix it",
9
+ "We need a test proving the save button disables itself while saving",
10
+ "Cover this list-filtering logic in the ViewModel with unit tests"
11
+ ],
12
+ "negative": [
13
+ "Write an XCTest for this SwiftUI view model's published property",
14
+ "Add a widget test for this Flutter StatefulWidget's local state",
15
+ "Write an xUnit test for this ASP.NET Core controller action",
16
+ "Add a React Testing Library test for this component's click handler",
17
+ "Write a pytest case for this FastAPI endpoint's validation logic",
18
+ "Review this Kotlin diff for exported manifest components before merge",
19
+ "Fix the failing Gradle build after this dependency version bump"
20
+ ]
21
+ },
22
+ "scenarios": [
23
+ {
24
+ "id": "mock-repository-boundary",
25
+ "prompt": "I have a ViewModel with a `loadProfile()` function that calls `profileRepository.fetch()`, a suspend function declared on a `ProfileRepository` interface. I want to unit test what happens when that call fails. How should I set up the mock?",
26
+ "strictness": "high",
27
+ "expected_behavior": [
28
+ {
29
+ "grader": "judge",
30
+ "rubric": "A correct answer mocks the ProfileRepository interface itself (with MockK, unless the project already standardizes on Mockito), stubbing its suspend fetch() function with a coroutine-aware form (e.g. coEvery { repository.fetch() } throws SomeException()) to simulate the failure, rather than reaching into the ViewModel under test to stub its own internal method or field.",
31
+ "pass_criteria": [
32
+ "Mocks the ProfileRepository interface (the collaborator/dependency), not the ViewModel under test's own internals.",
33
+ "Uses a coroutine-aware stubbing form for the suspend fetch() call (e.g. coEvery ... throws / coEvery ... returns), not the plain non-suspend every/when form.",
34
+ "States that the ViewModel is then exercised through its real loadProfile() call with the mocked repository injected, and the resulting error state is asserted."
35
+ ],
36
+ "fail_criteria": [
37
+ "Recommends stubbing or overriding a private method or field on the ViewModel under test itself instead of mocking the ProfileRepository dependency.",
38
+ "Uses a plain, non-coroutine-aware mocking call (e.g. every { repository.fetch() } ...) on a suspend function without a coroutine-aware form."
39
+ ]
40
+ }
41
+ ],
42
+ "calibration": {
43
+ "known_right": "Mock the `ProfileRepository` interface, not the ViewModel: `val repository = mockk<ProfileRepository>()`, then stub the failure with the coroutine-aware form since `fetch()` is a suspend function -- `coEvery { repository.fetch() } throws IOException()`. Construct the ViewModel with that mocked repository injected, call `viewModel.loadProfile()` inside `runTest { }`, advance the test dispatcher, and assert the ViewModel's exposed state ended up in its error variant. The mock stays at the repository boundary the whole way through; the ViewModel's own internals are exercised for real, which is what actually proves the error-handling path works.",
44
+ "known_wrong": "Since `loadProfile()` is public but calls a private `handleFetch()` helper internally, just use MockK's `spyk` on the ViewModel itself and stub `every { viewModel invoke \"handleFetch\" }` (or override the private method via reflection) to throw the exception directly -- that way you don't need to worry about setting up the repository interface at all, you're testing exactly the branch you care about.",
45
+ "vague": "Mock the dependency so the failure path runs and check that the ViewModel ends up in an error state.",
46
+ "subtle_wrong": "Mock the repository, but since `fetch()` is just a regular function call from the test's point of view, stub it the normal way: `every { repository.fetch() } throws IOException()`. It compiles and the test passes locally, so the coroutine-specific stubbing form isn't really necessary here."
47
+ }
48
+ },
49
+ {
50
+ "id": "suspend-function-runtest",
51
+ "prompt": "I want to test a suspend function in this repository class that calls `delay(2000)` before returning a cached value if the network is slow. What's the right way to test that without the test actually taking two seconds?",
52
+ "strictness": "high",
53
+ "expected_behavior": [
54
+ { "grader": "regex", "value": "runTest" },
55
+ {
56
+ "grader": "judge",
57
+ "rubric": "A correct answer wraps the test body in kotlinx-coroutines-test's runTest { }, which runs on a virtual clock that skips real delay time, so the suspend function's delay(2000) resolves instantly inside the test instead of the test actually pausing for two seconds; it does not rely on Thread.sleep or reducing the delay value in production code to make the test fast.",
58
+ "pass_criteria": [
59
+ "Names runTest { } as the mechanism that makes the test not actually wait for the delay.",
60
+ "Explains that runTest's virtual/skipped time is what avoids the real two-second wait, not a change to the delay duration itself.",
61
+ "Does not require changing the production delay(2000) call just to make the test faster."
62
+ ],
63
+ "fail_criteria": [
64
+ "Recommends using Thread.sleep, a shortened delay in a test-only build variant, or otherwise altering production code's delay value to make the test run fast.",
65
+ "Recommends running the suspend function with runBlocking and simply accepting the real two-second wait as unavoidable."
66
+ ]
67
+ }
68
+ ],
69
+ "calibration": {
70
+ "known_right": "Wrap the test body in `runTest { }` from `kotlinx-coroutines-test`. It runs the coroutine on a `TestScope` with a virtual clock, so when the code under test hits `delay(2000)`, the test doesn't actually pause for two real seconds -- the virtual clock just advances past it and the suspend function resumes immediately. Call the repository function directly inside `runTest { }`, e.g. `val result = repository.fetchWithCache()`, and assert on `result` right after; there's no need to touch the production `delay(2000)` call or introduce a test-only flag to shorten it, since `runTest` already makes the wait free without changing the code under test at all.",
71
+ "known_wrong": "Just run it under `runBlocking { }` like normal and accept that the test takes two seconds -- it's not a huge deal, and it's simpler than learning `runTest`'s virtual-time behavior. If it's really a problem, add a `testMode` flag to the repository that skips the `delay` call entirely when running under test, and set that flag from the test setup.",
72
+ "vague": "Use a coroutine test utility that lets the delay resolve fast instead of actually waiting.",
73
+ "subtle_wrong": "Use `runTest { }` for the test body, but also pass a shortened `delayMillis = 50` parameter into the repository function's signature for testing, since that guarantees the test is fast regardless of how `runTest` happens to handle the delay internally -- belt and suspenders."
74
+ }
75
+ }
76
+ ]
77
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "agents": [],
3
+ "note": "no pair -- honest DeepSeek deepseek-chat runner+judge gate (trials=10, strictness=high, flow 336) fails all 4 skills on trigger accuracy, never on behavior (every behavior scenario 1.0/1.0): swiftui-implementation TP 1/7 FP 0/7, swift-testing TP 3/7 FP 0/7, swift-code-review TP 2/6 FP 2/7 (a Swift diff wrongly routed to review when the prompt asked to implement a fix, despite the skill's own 'Read-only, no edits' description clause), swift-build-fix TP 1/6 FP 0/7. Genuine routing weakness against a crowded catalog, not an authoring defect -- stays experimental per flow 336's stack-pack-lessons.md rule against tuning descriptions to restate failing eval prompts."
4
+ }