[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6RAMDRZEERRPP9Y3J7NQBBQ":3,"skill-same:01M6RAMDRZEERRPP9Y3J7NQBBQ":63},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":14,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":48,"install":49,"resources":62},"01M6RAMDRZEERRPP9Y3J7NQBBQ","test-change",null,"Use when adding or reviewing behavioral tests for DevPulse features, state\u002Feffects, use cases, network boundaries, paging, persistence or coroutine lifetimes.","shounakmulay\u002Fdev-pulse","shounakmulay","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F42094964?v=4","01M6RAGFC9XZKE4TP55KEREM1B","Shounak Mulay","01M6RAGFJM9FHY8W0D92D9YRF1",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Ftest-change\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37],"test","change","test change","state","persistence","network","use","behavioral","tests","lifetimes","for","features","effects","when","2026-11-10 10:12:30",false,"---\nname: test-change\ndescription: Use when adding or reviewing behavioral tests for DevPulse features, state\u002Feffects, use cases, network boundaries, paging, persistence or coroutine lifetimes.\n---\n\n# Test the requested behavior\n\nRead the acceptance cases from [plan-change](..\u002Fplan-change\u002FSKILL.md) and the real implementation\u002Fcallers. Choose the smallest boundary that can expose the bug or prove the requested behavior. Keep tests in the affected module and do not add tests for trivial reversible edits or assertions that only mirror implementation.\n\n## Choose a real local example\n\n- [SafeRunCatchingTest](..\u002F..\u002F..\u002Fcore\u002Fcommon\u002Fsrc\u002FcommonTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fcommon\u002Fcoroutines\u002FSafeRunCatchingTest.kt): real blocks prove single execution, preserved ordinary failures and propagated cancellation. Counters here measure a visible side effect; they are not a production-state pattern.\n- [DevPulseNetworkClientTest](..\u002F..\u002F..\u002Fcore\u002Fnetwork\u002Fsrc\u002FcommonTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fnetwork\u002FDevPulseNetworkClientTest.kt): MockEngine exercises real decoding\u002Fheader\u002Ftext wrappers. Keep the serializer and mapper real.\n- [FeedDiscoveryDatabaseTest](..\u002F..\u002F..\u002Fcore\u002Fdata\u002Fdb\u002Fsrc\u002FjvmTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fdata\u002Fdb\u002FFeedDiscoveryDatabaseTest.kt): real Room assertions cover paging order, duplicate memberships, invalidation and populated reopen. Follow the current schema policy, not historical version numbers.\n- [DiscoveryFeedInteractorTest](..\u002F..\u002F..\u002Ffeature\u002Ffeed\u002Fsrc\u002FandroidHostTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Ffeature\u002Ffeed\u002Finteractor\u002Ffeed\u002FDiscoveryFeedInteractorTest.kt): real use cases\u002Finteractor with fake repository\u002Fqueue boundaries assert literal payloads, no duplicate enqueue, propagated failures\u002Fcancellation and switched\u002Fcancelled paging streams. Its ViewModel case controls virtual time, sets Main and Koin's SavedStateHandle, then cancels the ViewModel, stops Koin and resets Main in finally.\n\nDo not copy ExampleUnitTest\u002FExampleInstrumentedTest arithmetic assertions or an empty event handler as feature coverage. Existing state-serialization tests do not prove UI interaction.\n\n## Source sets and dependencies\n\nRead the affected build.gradle.kts and convention plugins first. commonTest runs shared behavior; jvmTest is appropriate for JVM-specific Room\u002Ffilesystem tests; androidHostTest covers Android\u002FRobolectric behavior only where withHostTestBuilder is configured. androidDeviceTest requires an actual device lane.\n\nUse kotlin.test, JUnit and coroutine-test APIs already wired in that module. Orbit test, Turbine or Mokkery availability varies by module; inspect dependencies before importing them. Do not introduce MockK or TestCoroutineDispatcher from obsolete templates. Both bounded Flow.first() and scheduled collectors occur in maintained tests; choose a method matching the lifetime being asserted instead of mandating a single library everywhere.\n\n## Assertions and isolation\n\nName tests Given-When-Then. Assert literal outcomes the caller observes, including relevant negative cases. Keep mappers, domain models, SQL and utility logic real; fake external\u002Frepository boundaries only when that boundary is not the behavior under test. Avoid relaxed mocks that manufacture successful results.\n\nFor flows\u002Fsearch, prove old streams stop after selection\u002Fquery changes, virtual debounce boundaries behave correctly, and cancelled jobs cannot emit stale results. For state\u002Feffects, call events and observe resulting state\u002Feffects through the current Orbit APIs. Establish a real subscription when the ViewModel starts work in bindStateSources\u002FrepeatOnSubscription.\n\nUse runTest\u002FtestScheduler and bounded waits instead of wall-clock sleeps. Clean up Main replacements, Koin, ViewModels, databases, files and owned clients in finally. Tests touching global DI\u002FMain\u002Fnative\u002Ffilesystem state run serially. Keep network\u002Fservice tests isolated unless live access is explicitly part of the requested validation.\n\nFor SQL\u002Fpersistence, use real Room queries and literal row IDs\u002Forder; include ties, scope changes and invalidation. For schema changes, follow [change-database](..\u002Fchange-database\u002FSKILL.md); a successful query-string assertion or fresh database open does not prove a populated migration.\n\n## Test both sides of the error boundary\n\n- A repository\u002Fclient\u002FDAO fake returns plain values and throws a chosen exception; it must not manufacture Result.failure or catch the exception it is meant to expose. A repository test asserts that technical failure throws, while the use-case test asserts Result.failure preserves that failure. These are different contracts.\n- A one-shot use case returns one Result\u003CT>, including Result\u003CUnit> for a command. Verify successful output, ordinary exception conversion, injected I\u002FO\u002Fdefault dispatcher choice, and cancellation propagation. A withContext-only forwarding method is insufficient to establish exception conversion.\n- An observed use case consumes a plain repository Flow\u003CT> and emits Flow\u003CResult\u003CT>> through flowCachingOnDefault. Verify successful values, upstream exception conversion, construction-time failure when relevant, cancellation and downstream exception transparency. The helper has no explicit CancellationException branch; test the concrete cancellation path and report a helper defect rather than silently weakening the contract or adding a repository catch.\n- When nesting use cases, assert the inner failure reaches the outer failure without Result\u003CResult\u003CT>> or a getOrNull()-induced success. Legitimate missing data, preference defaults and typed revision conflicts remain ordinary domain outcomes, not infrastructure failures.\n\nUse the settings setter\u002Fobserver pair linked from implement-change as the error-boundary blueprint. Some existing repository\u002Fread-state fixtures still use Result below the use case; adapt their boundary when that production contract is intentionally changed, rather than copying the violation. Do not alter application code or tests merely to refresh skill documentation.\n\n## Run and interpret\n\n```bash\nbash scripts\u002Fverify.sh --test :core:common dev.shounakmulay.devpulse.core.common.coroutines.SafeRunCatchingTest\nbash scripts\u002Fverify.sh --test :core:network dev.shounakmulay.devpulse.core.network.DevPulseNetworkClientTest\n```\n\nFor the existing Android discovery suite, task help confirms the class selector and fail-fast option. Inspect its task graph first and use an isolated checkout if schema\u002Fconfig writers occur:\n\n```bash\n.\u002Fgradlew --offline --no-daemon -Dorg.gradle.java.installations.auto-download=false :feature:feed:testAndroidHostTest --tests dev.shounakmulay.devpulse.feature.feed.interactor.feed.DiscoveryFeedInteractorTest --fail-fast\n```\n\nThese commands select a class, not all affected consumers. [verify-change](..\u002Fverify-change\u002FSKILL.md) selects broader checks. Compilation selectors have module scope; no configured native related-test selector exists. Report NO-SOURCE or unavailable source sets as missing evidence, not passing tests. Do not fix baseline failures by deleting assertions, blessing snapshots or broadening suppressions.\n\nFinish with UI\u002Fdevice interaction checks for visual, accessibility, navigation and native behavior. Report the actual command\u002Fresult and missing platforms. Never infer runtime behavior from compilation or fixture tests alone.\n","# Test the requested behavior\n\nRead the acceptance cases from [plan-change](..\u002Fplan-change\u002FSKILL.md) and the real implementation\u002Fcallers. Choose the smallest boundary that can expose the bug or prove the requested behavior. Keep tests in the affected module and do not add tests for trivial reversible edits or assertions that only mirror implementation.\n\n## Choose a real local example\n\n- [SafeRunCatchingTest](..\u002F..\u002F..\u002Fcore\u002Fcommon\u002Fsrc\u002FcommonTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fcommon\u002Fcoroutines\u002FSafeRunCatchingTest.kt): real blocks prove single execution, preserved ordinary failures and propagated cancellation. Counters here measure a visible side effect; they are not a production-state pattern.\n- [DevPulseNetworkClientTest](..\u002F..\u002F..\u002Fcore\u002Fnetwork\u002Fsrc\u002FcommonTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fnetwork\u002FDevPulseNetworkClientTest.kt): MockEngine exercises real decoding\u002Fheader\u002Ftext wrappers. Keep the serializer and mapper real.\n- [FeedDiscoveryDatabaseTest](..\u002F..\u002F..\u002Fcore\u002Fdata\u002Fdb\u002Fsrc\u002FjvmTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Fcore\u002Fdata\u002Fdb\u002FFeedDiscoveryDatabaseTest.kt): real Room assertions cover paging order, duplicate memberships, invalidation and populated reopen. Follow the current schema policy, not historical version numbers.\n- [DiscoveryFeedInteractorTest](..\u002F..\u002F..\u002Ffeature\u002Ffeed\u002Fsrc\u002FandroidHostTest\u002Fkotlin\u002Fdev\u002Fshounakmulay\u002Fdevpulse\u002Ffeature\u002Ffeed\u002Finteractor\u002Ffeed\u002FDiscoveryFeedInteractorTest.kt): real use cases\u002Finteractor with fake repository\u002Fqueue boundaries assert literal payloads, no duplicate enqueue, propagated failures\u002Fcancellation and switched\u002Fcancelled paging streams. Its ViewModel case controls virtual time, sets Main and Koin's SavedStateHandle, then cancels the ViewModel, stops Koin and resets Main in finally.\n\nDo not copy ExampleUnitTest\u002FExampleInstrumentedTest arithmetic assertions or an empty event handler as feature coverage. Existing state-serialization tests do not prove UI interaction.\n\n## Source sets and dependencies\n\nRead the affected build.gradle.kts and convention plugins first. commonTest runs shared behavior; jvmTest is appropriate for JVM-specific Room\u002Ffilesystem tests; androidHostTest covers Android\u002FRobolectric behavior only where withHostTestBuilder is configured. androidDeviceTest requires an actual device lane.\n\nUse kotlin.test, JUnit and coroutine-test APIs already wired in that module. Orbit test, Turbine or Mokkery availability varies by module; inspect dependencies before importing them. Do not introduce MockK or TestCoroutineDispatcher from obsolete templates. Both bounded Flow.first() and scheduled collectors occur in maintained tests; choose a method matching the lifetime being asserted instead of mandating a single library everywhere.\n\n## Assertions and isolation\n\nName tests Given-When-Then. Assert literal outcomes the caller observes, including relevant negative cases. Keep mappers, domain models, SQL and utility logic real; fake external\u002Frepository boundaries only when that boundary is not the behavior under test. Avoid relaxed mocks that manufacture successful results.\n\nFor flows\u002Fsearch, prove old streams stop after selection\u002Fquery changes, virtual debounce boundaries behave correctly, and cancelled jobs cannot emit stale results. For state\u002Feffects, call events and observe resulting state\u002Feffects through the current Orbit APIs. Establish a real subscription when the ViewModel starts work in bindStateSources\u002FrepeatOnSubscription.\n\nUse runTest\u002FtestScheduler and bounded waits instead of wall-clock sleeps. Clean up Main replacements, Koin, ViewModels, databases, files and owned clients in finally. Tests touching global DI\u002FMain\u002Fnative\u002Ffilesystem state run serially. Keep network\u002Fservice tests isolated unless live access is explicitly part of the requested validation.\n\nFor SQL\u002Fpersistence, use real Room queries and literal row IDs\u002Forder; include ties, scope changes and invalidation. For schema changes, follow [change-database](..\u002Fchange-database\u002FSKILL.md); a successful query-string assertion or fresh database open does not prove a populated migration.\n\n## Test both sides of the error boundary\n\n- A repository\u002Fclient\u002FDAO fake returns plain values and throws a chosen exception; it must not manufacture Result.failure or catch the exception it is meant to expose. A repository test asserts that technical failure throws, while the use-case test asserts Result.failure preserves that failure. These are different contracts.\n- A one-shot use case returns one Result\u003CT>, including Result\u003CUnit> for a command. Verify successful output, ordinary exception conversion, injected I\u002FO\u002Fdefault dispatcher choice, and cancellation propagation. A withContext-only forwarding method is insufficient to establish exception conversion.\n- An observed use case consumes a plain repository Flow\u003CT> and emits Flow\u003CResult\u003CT>> through flowCachingOnDefault. Verify successful values, upstream exception conversion, construction-time failure when relevant, cancellation and downstream exception transparency. The helper has no explicit CancellationException branch; test the concrete cancellation path and report a helper defect rather than silently weakening the contract or adding a repository catch.\n- When nesting use cases, assert the inner failure reaches the outer failure without Result\u003CResult\u003CT>> or a getOrNull()-induced success. Legitimate missing data, preference defaults and typed revision conflicts remain ordinary domain outcomes, not infrastructure failures.\n\nUse the settings setter\u002Fobserver pair linked from implement-change as the error-boundary blueprint. Some existing repository\u002Fread-state fixtures still use Result below the use case; adapt their boundary when that production contract is intentionally changed, rather than copying the violation. Do not alter application code or tests merely to refresh skill documentation.\n\n## Run and interpret\n\n```bash\nbash scripts\u002Fverify.sh --test :core:common dev.shounakmulay.devpulse.core.common.coroutines.SafeRunCatchingTest\nbash scripts\u002Fverify.sh --test :core:network dev.shounakmulay.devpulse.core.network.DevPulseNetworkClientTest\n```\n\nFor the existing Android discovery suite, task help confirms the class selector and fail-fast option. Inspect its task graph first and use an isolated checkout if schema\u002Fconfig writers occur:\n\n```bash\n.\u002Fgradlew --offline --no-daemon -Dorg.gradle.java.installations.auto-download=false :feature:feed:testAndroidHostTest --tests dev.shounakmulay.devpulse.feature.feed.interactor.feed.DiscoveryFeedInteractorTest --fail-fast\n```\n\nThese commands select a class, not all affected consumers. [verify-change](..\u002Fverify-change\u002FSKILL.md) selects broader checks. Compilation selectors have module scope; no configured native related-test selector exists. Report NO-SOURCE or unavailable source sets as missing evidence, not passing tests. Do not fix baseline failures by deleting assertions, blessing snapshots or broadening suppressions.\n\nFinish with UI\u002Fdevice interaction checks for visual, accessibility, navigation and native behavior. Report the actual command\u002Fresult and missing platforms. Never infer runtime behavior from compilation or fixture tests alone.\n",{"name":5,"description":7},"main","0793a3a80d3c675dc6a6cbba3bf45282f4908ba8",".agents\u002Fskills\u002Ftest-change","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse","https:\u002F\u002Fgithub.com\u002Fshounakmulay","2026-11-04 00:07:33",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add shounakmulay\u002Fdev-pulse --skill test-change",{"tool":55,"command":56},"bunx","bunx skills add shounakmulay\u002Fdev-pulse --skill test-change",{"tool":58,"command":59},"pnpm","pnpm dlx skills add shounakmulay\u002Fdev-pulse --skill test-change","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Ftest-change\u002FSKILL.md",true,[],[64,81,87,106,121,136],{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":70,"publishedAt":38},"01M6RAQ4Q5AT2M8ZMJCJZAR5SE","verify-change","Use when verifying DevPulse changes, choosing scoped Gradle checks, reviewing database or feed changes, or reporting platform verification gaps.","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fverify-change\u002FSKILL.md",[],[25,71,72,37,73,74,75,76,77,78,30,79,80],"verify","verify change","gaps","scoped","changes","platform","gradle","verification","reporting","database",{"skillNo":82,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":86,"publishedAt":38},"01M6RAPQ2X544N3R27SE4B05YC","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Ftest-change\u002FSKILL.md",[85],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[24,25,26,28,27,29,30,31,32,33,34,35,36,37],{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":38},"01M6RAPKGMDSNA3SGHXHCXTC3F","plan-change","Use when planning a DevPulse feature, bug fix or refactor, clarifying ownership and edge cases, or deciding the implementation and acceptance checks.","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fplan-change\u002FSKILL.md",[],[94,25,95,30,96,97,98,99,37,100,101,102,103,104,105],"plan","plan change","bug","implementation","feature","ownership","bug fix","refactor","edge","acceptance","planning","fix",{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":38},"01M6RAPCNYVAT630PS59NZ5DET","navigation-3","Use when changing DevPulse Navigation3 routes, tab stacks, adaptive list\u002Fdetail metadata or back behavior.","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fnavigation-3\u002FSKILL.md",[],[113,114,115,37,116,117,30,118,119,120],"navigation 3","navigation","back","adaptive","detail","behavior","list","metadata",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":38},"01M6RAP8GK2PND12M3PEE8GB1Q","ktlint-fix","Use when explicitly asked to run the legacy ktlint workflow; first check whether formatting tasks actually exist.","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fktlint-fix\u002FSKILL.md",[],[128,105,129,130,131,132,133,30,37,134,135],"ktlint fix","check","first","run","tasks","workflow","formatting","Legacy",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":142,"publishedAt":38},"01M6RAP50MQZHW4ZBA344H5TTV","implement-change","Use when implementing or refactoring existing DevPulse behavior across screens, state, use cases, repositories or shared utilities after establishing acceptance checks.","https:\u002F\u002Fgithub.com\u002Fshounakmulay\u002Fdev-pulse\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fimplement-change\u002FSKILL.md",[],[25,143,144,145,37,146,147,148,149,150,30,118,151,103,27],"implement change","implement","implementing","shared utilities","Existing","Utilities","shared","across","refactoring"]